From 173e306332e76a9fb995d8c4a3499e3cce2cb79d Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Wed, 19 Aug 2020 12:21:28 -0600 Subject: [PATCH 01/16] Added license, contribution instructions --- CNAME | 1 - CONTRIBUTING.md | 191 +++++++++++++++++++++++++++++++++++++++++++++ LICENSE | 201 ++++++++++++++++++++++++++++++++++++++++++++++++ _config.yml | 1 - 4 files changed, 392 insertions(+), 2 deletions(-) delete mode 100644 CNAME create mode 100644 CONTRIBUTING.md create mode 100644 LICENSE delete mode 100644 _config.yml diff --git a/CNAME b/CNAME deleted file mode 100644 index 79c78efca..000000000 --- a/CNAME +++ /dev/null @@ -1 +0,0 @@ -docs.jaredhendrickson.com \ No newline at end of file diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 000000000..06b0d1e7c --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,191 @@ +How to Contribute +================= +Thank you for your interest in contributing to this project! Community support is essential to keep this package secure, +efficient, and useful. Your contributions are very much appreciated. To ensure this project can achieve and retain a +production ready package, please follow the guidelines detailed in this document. + +Requirements +------------ +If you plan on contributing, please follow these basic requirements +- Do not introduce known vulnerabilities. Please do your due diligence before integrating anything that may be insecure. +- Be respectful. Open source contributions are a learning experience for all developers regardless of skill level. + Please be kind and helpful towards other contributors. +- Write ethical code. Please do not steal code and claim it as your own. + +Testing +------- +It is preferred that any API endpoints or core features of this package include unit tests at the time they are written. +This will help speed up pull requests, and assist in future regression testing. For example, if you write a new API +endpoint please include a unit test that will thoroughly test the extent of that endpoint. Python3 is the preferred +language for unit tests. Seeing as changes will primarily be to API endpoints, please consider Python3's `requests` +module. + +Proposing Changes +----------------- +A pull request is required for any proposed change. It is preferred that you fork the main project, make your changes, +then submit a pull request to merge your changes to the main project's current development branch. Once merged, your +changes will be made available upon the next release. + +Coding Conventions +------------------ +Make an attempt to match the format of existing code. The basic conventions are: +- Comments are recommended as long as they are brief, meaningful, and short +- Variables should be defined using snake case (e.g. snake_case) +- Functions should be defined using snake case (e.g. snake_case()) +- Constants should be defined using upper snake case (e.g. SNAKE_CASE) +- Classes should defined using pascal case (e.g. PascalCase) +- Lines should not contain more than 128 characters
+_Note: suggestions to coding conventions are welcome, refactoring as things change is necessary to support maintainable +code_ + +Writing new API Endpoints +--------------------- +Most contributions to this project will be in the form of integrating new API endpoints. API endpoints are comprised of +a few different components. It is strongly recommended that you familiarize yourself with pfSense's PHP shell before +diving into creating endpoints. To get started writing your own endpoints, please follow the steps below: + +### Things to Know ### + - The API is based on REST principals. Unfortunately, pfSense does not allow any custom changes to the NGINX + configuration so alternate request methods like `PUT` and `DELETE` do not appear to be possible. To accommodate this, + the requested action must be defined in the endpoint path. + - Create actions must be a `POST` request to an endpoint ending in `/add/` + (e.g. `https://localhost/api/v1/firewall/rules/add/`) + - Read actions must be a `GET` request to a base endpoint (e.g. `https://localhost/api/v1/firewall/rules/`) + - Update actions must be a `POST` request to an endpoint ending in `/update/` + (e.g. `https://localhost/api/v1/firewall/rules/update/`) + - Delete actions must be a `POST` request to an endpoint ending in `/delete/` + (e.g. `https://localhost/api/v1/firewall/rules/delete/`) + +### Writing the API caller ### +At the core of the API endpoint is the API caller. This is a function that validates client request data, writes changes +to the pfSense configuration file, makes any corresponding system changes, and returns the requested data as an array. + +1. Most API endpoints are designed to allow programmatic changes to configuration that is usually made in the pfSense +webConfigurator. To get a basic understanding on what your API call will need to do, look at the PHP code of the +corresponding webConfigurator page (found within `/usr/local/www/` of your pfSense installation). You should be able to +get a good idea of what input validation is needed, and what core pfSense functions you will need to call to achieve +these changes. You can also use existing API call functions (found in `/files/etc/inc/apicalls.inc` within this repo) as +a reference! + +2. Write your function in `/files/etc/inc/apicalls.inc`. The function name should match the URL filepath without the +version. For example, for an API endpoint at URL `/api/v1/firewall/rules/delete/`, the function would be named +`api_firewall_rules_delete`. Please also place this function next to any related functions in `apicall.inc`. For example +if you write a new API call function for `api_firewall_rules_update`, place it next to any existing functions for the +API firewall calls. + +3. Ensure API callers always return the data of the action that was performed. For example, if you are writing an +`update` endpoint, ensure the API callers returns the updated data. Or if you are writing a `delete` endpoint, ensure +the API caller always returns the deleted data. + +4. Ensure any validation or API errors encountered when the API caller function is run returns a corresponding error +from the `/files/etc/inc/apiresp.inc` file. You can read more about writing API error responses below. + +Here is an example structure of an API caller function: +``` +function api_caller_function() { + # VARIABLES + global $err_lib, $config, $api_resp, $client_params; + $read_only_action = true; // Set whether this action requires read only access + $req_privs = array("page-all", "page-some-other-webconfigurator-permission"); // Array of privs allowed + $http_method = $_SERVER['REQUEST_METHOD']; // Get our HTTP method + + # RUN TIME + // Check that client is authenticated and authorized + if (api_authorized($req_privs, $read_only_action)) { + // Check that our HTTP method is GET (READ) + if ($http_method === 'GET') { + // ADD YOUR INPUT VALIDATION, CONFIG WRITES, AND SYSTEM CONFIGURATION + // Return our response + $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => []); + return $api_resp; + } else { + $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); + $api_resp["message"] = $err_lib[$api_resp["return"]]; + return $api_resp; + } + } else { + return $api_resp; // Returns default unauthorized response + } +} +``` + +### Writing API responses ### +The API uses a centralized API response array (found in `/file/etc/inc/apiresp.inc` of this repo). Each response +corresponds with a unique code that can be used to get the response message, status, etc. This is particularly helpful +when API response messages need to be changed as it is always in one central location. + +To create a new API response: + +1. Pick a numeric code that is not already in use in the `$err_lib` array within the `api_error_lib()` function of +`/files/etc/inc/apiresp.inc`. Try to use a code that is within the reserved range of the API endpoint you are creating. +2. Add a new array item to the `$err_lib` array within the `api_error_lib()`. The associated ID should be the numeric +code you picked, and the value should be the descriptive message to return from the API. Some examples are: +``` +$err_lib = array( + // 0-999 reserved for system API level responses + 0 => "Success", + 1 => "Process encountered unexpected error", + 2 => "Invalid HTTP method", + 3 => "Authentication failed" +) +``` +3. To get the response message, ensure your API caller function has `$err_lib` declared globally +(e.g. `global $err_lib;`), then you can pull the message corresponding with your code as such `$err_lib[]`. Each +API caller should return a response error similar to: +``` +array( + "status" => "unauthorized", # Sets the descriptive HTTP response message (unauthorized, not found, ok, etc.) + "code" => 401, # Sets the HTTP response code. This will be the response code PHP returns to the client. + "return" => 3, # Set the unique API response code from `apiresp.inc` + "message" => $err_lib[3], # Pull the message corresponding with this unique response code from `apiresp.inc` + "data" => [] # Set the data to return to the client in an array format +);` +``` + +### Writing API endpoint listeners ### +Each API caller must have an API endpoint listener within the web path to listen for requests and execute the API caller +function. These can be found in `/files/usr/local/www/api/v1/`. To create a new endpoint: + +1. Create a new directory in `/files/usr/local/www/api/v1/` that corresponds with the endpoint you are creating. For +example, if you are creating a new endpoint that deletes a firewall rule, you would create the directory +`/files/usr/local/www/api/v1/firewall/rules/delete/`. +2. Create an index.php file within that directory and add the following code: +``` + Date: Wed, 19 Aug 2020 12:38:52 -0600 Subject: [PATCH 02/16] Fixed bug that returned null when trying to delete an unbound host override that didn't exist --- pfSense-pkg-API/files/etc/inc/apicalls.inc | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pfSense-pkg-API/files/etc/inc/apicalls.inc b/pfSense-pkg-API/files/etc/inc/apicalls.inc index 2cb69d3f3..b1e3a7a97 100644 --- a/pfSense-pkg-API/files/etc/inc/apicalls.inc +++ b/pfSense-pkg-API/files/etc/inc/apicalls.inc @@ -5194,8 +5194,9 @@ function api_services_unbound_delete_hosts() { return $api_resp; } } else { - //to do - return; + $api_resp = array("status" => "bad request", "code" => 400, "return" => 2013); + $api_resp["message"] = $err_lib[$api_resp["return"]]; + return $api_resp; } } else { $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); From 14091f2552f9bd596fad7a43da9dee9ffce483c1 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Thu, 20 Aug 2020 01:05:18 -0600 Subject: [PATCH 03/16] Added support for JWT authentication, updated API webConfigurator page to allow admins to rotate the JWT server key and set the JWT expiration period, updated makefile to include JWT changes and prep for version 0.0.3 --- pfSense-pkg-API/Makefile | 28 +- pfSense-pkg-API/files/etc/inc/api.inc | 68 ++- pfSense-pkg-API/files/etc/inc/apicalls.inc | 15 + pfSense-pkg-API/files/etc/inc/apiresp.inc | 2 + pfSense-pkg-API/files/etc/inc/php-jwt/LICENSE | 30 + .../files/etc/inc/php-jwt/README.md | 200 +++++++ .../files/etc/inc/php-jwt/composer.json | 33 ++ .../inc/php-jwt/src/BeforeValidException.php | 6 + .../etc/inc/php-jwt/src/ExpiredException.php | 6 + .../files/etc/inc/php-jwt/src/JWK.php | 171 ++++++ .../files/etc/inc/php-jwt/src/JWT.php | 512 ++++++++++++++++++ .../php-jwt/src/SignatureInvalidException.php | 6 + pfSense-pkg-API/files/pkg-deinstall.in | 1 + .../usr/local/share/pfSense-pkg-API/info.xml | 2 + .../files/usr/local/www/api/index.php | 125 +++-- .../local/www/api/v1/access_token/index.php | 10 + pfSense-pkg-API/pkg-plist | 13 + 17 files changed, 1173 insertions(+), 55 deletions(-) create mode 100644 pfSense-pkg-API/files/etc/inc/php-jwt/LICENSE create mode 100644 pfSense-pkg-API/files/etc/inc/php-jwt/README.md create mode 100644 pfSense-pkg-API/files/etc/inc/php-jwt/composer.json create mode 100644 pfSense-pkg-API/files/etc/inc/php-jwt/src/BeforeValidException.php create mode 100644 pfSense-pkg-API/files/etc/inc/php-jwt/src/ExpiredException.php create mode 100644 pfSense-pkg-API/files/etc/inc/php-jwt/src/JWK.php create mode 100644 pfSense-pkg-API/files/etc/inc/php-jwt/src/JWT.php create mode 100644 pfSense-pkg-API/files/etc/inc/php-jwt/src/SignatureInvalidException.php create mode 100644 pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php diff --git a/pfSense-pkg-API/Makefile b/pfSense-pkg-API/Makefile index 2c846086f..a1838cd41 100644 --- a/pfSense-pkg-API/Makefile +++ b/pfSense-pkg-API/Makefile @@ -2,7 +2,7 @@ PORTNAME= pfSense-pkg-API PORTVERSION= 0.0 -PORTREVISION= 2 +PORTREVISION= 3 CATEGORIES= sysutils MASTER_SITES= # empty DISTFILES= # empty @@ -19,6 +19,8 @@ do-extract: ${MKDIR} ${WRKSRC} do-install: + + # INSTALL OUR API INCLUDE FILE ${MKDIR} ${STAGEDIR}/etc/inc ${INSTALL_DATA} ${FILESDIR}/etc/inc/api.inc \ @@ -27,6 +29,25 @@ do-install: ${STAGEDIR}/etc/inc ${INSTALL_DATA} ${FILESDIR}/etc/inc/apiresp.inc \ ${STAGEDIR}/etc/inc + # INSTALL STATIC PHP-JWT FILES (static files are required as pfSense does not allow composer installations) + ${MKDIR} ${STAGEDIR}/etc/inc/php-jwt + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/README.md \ + ${STAGEDIR}/etc/inc/php-jwt + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/LICENSE \ + ${STAGEDIR}/etc/inc/php-jwt + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/composer.json \ + ${STAGEDIR}/etc/inc/php-jwt + ${MKDIR} ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/JWT.php \ + ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/JWK.php \ + ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/SignatureInvalidException.php \ + ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/ExpiredException.php \ + ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/BeforeValidException.php \ + ${STAGEDIR}/etc/inc/php-jwt/src # INSTALL OUR PFSENSE PKG ${MKDIR} ${STAGEDIR}${PREFIX}/pkg @@ -40,6 +61,11 @@ do-install: ${STAGEDIR}${PREFIX}/www/api # API version ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1 + # ACCESS TOKEN API ENDPOINTS + # Access Token base + ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/access_token + ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/access_token/index.php \ + ${STAGEDIR}${PREFIX}/www/api/v1/access_token # USERS API ENDPOINTS------------------------------------------ # Users base ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users diff --git a/pfSense-pkg-API/files/etc/inc/api.inc b/pfSense-pkg-API/files/etc/inc/api.inc index e2ced5cf7..2238caf55 100644 --- a/pfSense-pkg-API/files/etc/inc/api.inc +++ b/pfSense-pkg-API/files/etc/inc/api.inc @@ -1,5 +1,9 @@ $config["system"]["hostname"], + "exp" => time() + $token_exp, + "nbf" => time(), + "data" => $data + ); + return JWT::encode($payload, $api_config["server_key"]); +} + +// Decodes a JWT +function api_decode_jwt($token) { + $key = get_api_configuration()[1]["server_key"]; // Save our current server key + try { + $decoded = (array) JWT::decode($token, $key, array('HS256')); + } catch (Exception $e) { + $decoded = false; + } + return $decoded; +} + // Get our API token ID for a given username function api_get_existing_tokens($username) { // Local variables @@ -143,6 +190,23 @@ function api_authenticate() { $authenticated = false; $api_config = get_api_configuration()[1]; $users = index_users(); + + // Check for JWT in Authorization header if authmode is set to JWT + if ($api_config["authmode"] === "jwt") { + $auth_header = explode(" ", $_SERVER["HTTP_AUTHORIZATION"]); + $token_type = $auth_header[0]; + $token = $auth_header[1]; + $decoded_jwt = api_decode_jwt($token); + + // Check that our JWT from our Authorization header is valid + if ($token_type === "Bearer" and $decoded_jwt !== false) { + unset($_SESSION["Username"]); + $client_id = $decoded_jwt["data"]; + $_SESSION["Username"] = $client_id; + $authenticated = true; + } + } + // Format our client ID and token based on our configured auth mode if ($api_config["authmode"] === "base64") { $client_id = base64_decode($client_id); @@ -312,7 +376,7 @@ function api_whitelist_check() { } elseif ($srv_ip === $if_info["ipaddrv6"]) { $allowed = true; break; - } elseif ($srv_ip === "127.0.0.1" and $wif === "localhost") { + } elseif (in_array($srv_ip, ["::1", "127.0.0.1", "localhost"]) and $wif === "localhost") { $allowed = true; break; }elseif ($wif === "any") { @@ -326,7 +390,7 @@ function api_whitelist_check() { $api_resp = array("status" => "forbidden", "code" => 403, "return" => 6); $api_resp["message"] = $err_lib[$api_resp["return"]]; http_response_code(403); - echo json_encode($api_resp); + echo json_encode($api_resp).PHP_EOL; die; } } diff --git a/pfSense-pkg-API/files/etc/inc/apicalls.inc b/pfSense-pkg-API/files/etc/inc/apicalls.inc index b1e3a7a97..1b8ab4abd 100644 --- a/pfSense-pkg-API/files/etc/inc/apicalls.inc +++ b/pfSense-pkg-API/files/etc/inc/apicalls.inc @@ -19,6 +19,21 @@ api_runtime_allowed(); // Check that our configuration allows this API call t session_start(); // Start our session. This is only used for tracking user name $pf_ver_num = get_pfsense_version()["program"]; // Pull our full pfSense version +function api_access_token() { + global $api_resp, $client_params, $err_lib; + # Check that auth mode is set to JWT before creating token + if (get_api_configuration()[1]["authmode"] === "jwt") { + # Return our JWT if user is authenticated + if (authenticate_user($client_params["username"], $client_params["password"])) { + $jwt = api_create_jwt($client_params["username"]); + $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => ["token" => $jwt]); + } + } else { + $api_resp = array("status" => "forbidden", "code" => 403, "return" => 9, "message" => $err_lib[9], "data" => []); + } + return $api_resp; +} + function api_status_carp() { # VARIABLES global $err_lib, $config, $api_resp, $client_params; diff --git a/pfSense-pkg-API/files/etc/inc/apiresp.inc b/pfSense-pkg-API/files/etc/inc/apiresp.inc index cb73d229a..48a6941f3 100644 --- a/pfSense-pkg-API/files/etc/inc/apiresp.inc +++ b/pfSense-pkg-API/files/etc/inc/apiresp.inc @@ -12,6 +12,8 @@ function api_error_lib() { 6 => "Forbidden", 7 => "Search attribute not found", 8 => "Could not locate pfSense version", + 9 => "Authentication mode must be set to JWT to enable access token authentication", + // 1000-1999 reserved for /system API calls 1000 => "Invalid system hostname", 1001 => "Invalid system hostname domain", diff --git a/pfSense-pkg-API/files/etc/inc/php-jwt/LICENSE b/pfSense-pkg-API/files/etc/inc/php-jwt/LICENSE new file mode 100644 index 000000000..cb0c49b33 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/php-jwt/LICENSE @@ -0,0 +1,30 @@ +Copyright (c) 2011, Neuman Vong + +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + + * Neither the name of Neuman Vong nor the names of other + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/pfSense-pkg-API/files/etc/inc/php-jwt/README.md b/pfSense-pkg-API/files/etc/inc/php-jwt/README.md new file mode 100644 index 000000000..9c8b5455b --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/php-jwt/README.md @@ -0,0 +1,200 @@ +[![Build Status](https://travis-ci.org/firebase/php-jwt.png?branch=master)](https://travis-ci.org/firebase/php-jwt) +[![Latest Stable Version](https://poser.pugx.org/firebase/php-jwt/v/stable)](https://packagist.org/packages/firebase/php-jwt) +[![Total Downloads](https://poser.pugx.org/firebase/php-jwt/downloads)](https://packagist.org/packages/firebase/php-jwt) +[![License](https://poser.pugx.org/firebase/php-jwt/license)](https://packagist.org/packages/firebase/php-jwt) + +PHP-JWT +======= +A simple library to encode and decode JSON Web Tokens (JWT) in PHP, conforming to [RFC 7519](https://tools.ietf.org/html/rfc7519). + +Installation +------------ + +Use composer to manage your dependencies and download PHP-JWT: + +```bash +composer require firebase/php-jwt +``` + +Example +------- +```php + "http://example.org", + "aud" => "http://example.com", + "iat" => 1356999524, + "nbf" => 1357000000 +); + +/** + * IMPORTANT: + * You must specify supported algorithms for your application. See + * https://tools.ietf.org/html/draft-ietf-jose-json-web-algorithms-40 + * for a list of spec-compliant algorithms. + */ +$jwt = JWT::encode($payload, $key); +$decoded = JWT::decode($jwt, $key, array('HS256')); + +print_r($decoded); + +/* + NOTE: This will now be an object instead of an associative array. To get + an associative array, you will need to cast it as such: +*/ + +$decoded_array = (array) $decoded; + +/** + * You can add a leeway to account for when there is a clock skew times between + * the signing and verifying servers. It is recommended that this leeway should + * not be bigger than a few minutes. + * + * Source: http://self-issued.info/docs/draft-ietf-oauth-json-web-token.html#nbfDef + */ +JWT::$leeway = 60; // $leeway in seconds +$decoded = JWT::decode($jwt, $key, array('HS256')); + +?> +``` +Example with RS256 (openssl) +---------------------------- +```php + "example.org", + "aud" => "example.com", + "iat" => 1356999524, + "nbf" => 1357000000 +); + +$jwt = JWT::encode($payload, $privateKey, 'RS256'); +echo "Encode:\n" . print_r($jwt, true) . "\n"; + +$decoded = JWT::decode($jwt, $publicKey, array('RS256')); + +/* + NOTE: This will now be an object instead of an associative array. To get + an associative array, you will need to cast it as such: +*/ + +$decoded_array = (array) $decoded; +echo "Decode:\n" . print_r($decoded_array, true) . "\n"; +?> +``` + +Changelog +--------- + +#### 5.0.0 / 2017-06-26 +- Support RS384 and RS512. + See [#117](https://github.com/firebase/php-jwt/pull/117). Thanks [@joostfaassen](https://github.com/joostfaassen)! +- Add an example for RS256 openssl. + See [#125](https://github.com/firebase/php-jwt/pull/125). Thanks [@akeeman](https://github.com/akeeman)! +- Detect invalid Base64 encoding in signature. + See [#162](https://github.com/firebase/php-jwt/pull/162). Thanks [@psignoret](https://github.com/psignoret)! +- Update `JWT::verify` to handle OpenSSL errors. + See [#159](https://github.com/firebase/php-jwt/pull/159). Thanks [@bshaffer](https://github.com/bshaffer)! +- Add `array` type hinting to `decode` method + See [#101](https://github.com/firebase/php-jwt/pull/101). Thanks [@hywak](https://github.com/hywak)! +- Add all JSON error types. + See [#110](https://github.com/firebase/php-jwt/pull/110). Thanks [@gbalduzzi](https://github.com/gbalduzzi)! +- Bugfix 'kid' not in given key list. + See [#129](https://github.com/firebase/php-jwt/pull/129). Thanks [@stampycode](https://github.com/stampycode)! +- Miscellaneous cleanup, documentation and test fixes. + See [#107](https://github.com/firebase/php-jwt/pull/107), [#115](https://github.com/firebase/php-jwt/pull/115), + [#160](https://github.com/firebase/php-jwt/pull/160), [#161](https://github.com/firebase/php-jwt/pull/161), and + [#165](https://github.com/firebase/php-jwt/pull/165). Thanks [@akeeman](https://github.com/akeeman), + [@chinedufn](https://github.com/chinedufn), and [@bshaffer](https://github.com/bshaffer)! + +#### 4.0.0 / 2016-07-17 +- Add support for late static binding. See [#88](https://github.com/firebase/php-jwt/pull/88) for details. Thanks to [@chappy84](https://github.com/chappy84)! +- Use static `$timestamp` instead of `time()` to improve unit testing. See [#93](https://github.com/firebase/php-jwt/pull/93) for details. Thanks to [@josephmcdermott](https://github.com/josephmcdermott)! +- Fixes to exceptions classes. See [#81](https://github.com/firebase/php-jwt/pull/81) for details. Thanks to [@Maks3w](https://github.com/Maks3w)! +- Fixes to PHPDoc. See [#76](https://github.com/firebase/php-jwt/pull/76) for details. Thanks to [@akeeman](https://github.com/akeeman)! + +#### 3.0.0 / 2015-07-22 +- Minimum PHP version updated from `5.2.0` to `5.3.0`. +- Add `\Firebase\JWT` namespace. See +[#59](https://github.com/firebase/php-jwt/pull/59) for details. Thanks to +[@Dashron](https://github.com/Dashron)! +- Require a non-empty key to decode and verify a JWT. See +[#60](https://github.com/firebase/php-jwt/pull/60) for details. Thanks to +[@sjones608](https://github.com/sjones608)! +- Cleaner documentation blocks in the code. See +[#62](https://github.com/firebase/php-jwt/pull/62) for details. Thanks to +[@johanderuijter](https://github.com/johanderuijter)! + +#### 2.2.0 / 2015-06-22 +- Add support for adding custom, optional JWT headers to `JWT::encode()`. See +[#53](https://github.com/firebase/php-jwt/pull/53/files) for details. Thanks to +[@mcocaro](https://github.com/mcocaro)! + +#### 2.1.0 / 2015-05-20 +- Add support for adding a leeway to `JWT:decode()` that accounts for clock skew +between signing and verifying entities. Thanks to [@lcabral](https://github.com/lcabral)! +- Add support for passing an object implementing the `ArrayAccess` interface for +`$keys` argument in `JWT::decode()`. Thanks to [@aztech-dev](https://github.com/aztech-dev)! + +#### 2.0.0 / 2015-04-01 +- **Note**: It is strongly recommended that you update to > v2.0.0 to address + known security vulnerabilities in prior versions when both symmetric and + asymmetric keys are used together. +- Update signature for `JWT::decode(...)` to require an array of supported + algorithms to use when verifying token signatures. + + +Tests +----- +Run the tests using phpunit: + +```bash +$ pear install PHPUnit +$ phpunit --configuration phpunit.xml.dist +PHPUnit 3.7.10 by Sebastian Bergmann. +..... +Time: 0 seconds, Memory: 2.50Mb +OK (5 tests, 5 assertions) +``` + +New Lines in private keys +----- + +If your private key contains `\n` characters, be sure to wrap it in double quotes `""` +and not single quotes `''` in order to properly interpret the escaped characters. + +License +------- +[3-Clause BSD](http://opensource.org/licenses/BSD-3-Clause). diff --git a/pfSense-pkg-API/files/etc/inc/php-jwt/composer.json b/pfSense-pkg-API/files/etc/inc/php-jwt/composer.json new file mode 100644 index 000000000..25d1cfa96 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/php-jwt/composer.json @@ -0,0 +1,33 @@ +{ + "name": "firebase/php-jwt", + "description": "A simple library to encode and decode JSON Web Tokens (JWT) in PHP. Should conform to the current spec.", + "homepage": "https://github.com/firebase/php-jwt", + "keywords": [ + "php", + "jwt" + ], + "authors": [ + { + "name": "Neuman Vong", + "email": "neuman+pear@twilio.com", + "role": "Developer" + }, + { + "name": "Anant Narayanan", + "email": "anant@php.net", + "role": "Developer" + } + ], + "license": "BSD-3-Clause", + "require": { + "php": ">=5.3.0" + }, + "autoload": { + "psr-4": { + "Firebase\\JWT\\": "src" + } + }, + "require-dev": { + "phpunit/phpunit": ">=4.8 <=9" + } +} diff --git a/pfSense-pkg-API/files/etc/inc/php-jwt/src/BeforeValidException.php b/pfSense-pkg-API/files/etc/inc/php-jwt/src/BeforeValidException.php new file mode 100644 index 000000000..fdf82bd94 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/php-jwt/src/BeforeValidException.php @@ -0,0 +1,6 @@ + + * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD + * @link https://github.com/firebase/php-jwt + */ +class JWK +{ + /** + * Parse a set of JWK keys + * + * @param array $jwks The JSON Web Key Set as an associative array + * + * @return array An associative array that represents the set of keys + * + * @throws InvalidArgumentException Provided JWK Set is empty + * @throws UnexpectedValueException Provided JWK Set was invalid + * @throws DomainException OpenSSL failure + * + * @uses parseKey + */ + public static function parseKeySet(array $jwks) + { + $keys = array(); + + if (!isset($jwks['keys'])) { + throw new UnexpectedValueException('"keys" member must exist in the JWK Set'); + } + if (empty($jwks['keys'])) { + throw new InvalidArgumentException('JWK Set did not contain any keys'); + } + + foreach ($jwks['keys'] as $k => $v) { + $kid = isset($v['kid']) ? $v['kid'] : $k; + if ($key = self::parseKey($v)) { + $keys[$kid] = $key; + } + } + + if (0 === \count($keys)) { + throw new UnexpectedValueException('No supported algorithms found in JWK Set'); + } + + return $keys; + } + + /** + * Parse a JWK key + * + * @param array $jwk An individual JWK + * + * @return resource|array An associative array that represents the key + * + * @throws InvalidArgumentException Provided JWK is empty + * @throws UnexpectedValueException Provided JWK was invalid + * @throws DomainException OpenSSL failure + * + * @uses createPemFromModulusAndExponent + */ + private static function parseKey(array $jwk) + { + if (empty($jwk)) { + throw new InvalidArgumentException('JWK must not be empty'); + } + if (!isset($jwk['kty'])) { + throw new UnexpectedValueException('JWK must contain a "kty" parameter'); + } + + switch ($jwk['kty']) { + case 'RSA': + if (\array_key_exists('d', $jwk)) { + throw new UnexpectedValueException('RSA private keys are not supported'); + } + if (!isset($jwk['n']) || !isset($jwk['e'])) { + throw new UnexpectedValueException('RSA keys must contain values for both "n" and "e"'); + } + + $pem = self::createPemFromModulusAndExponent($jwk['n'], $jwk['e']); + $publicKey = \openssl_pkey_get_public($pem); + if (false === $publicKey) { + throw new DomainException( + 'OpenSSL error: ' . \openssl_error_string() + ); + } + return $publicKey; + default: + // Currently only RSA is supported + break; + } + } + + /** + * Create a public key represented in PEM format from RSA modulus and exponent information + * + * @param string $n The RSA modulus encoded in Base64 + * @param string $e The RSA exponent encoded in Base64 + * + * @return string The RSA public key represented in PEM format + * + * @uses encodeLength + */ + private static function createPemFromModulusAndExponent($n, $e) + { + $modulus = JWT::urlsafeB64Decode($n); + $publicExponent = JWT::urlsafeB64Decode($e); + + $components = array( + 'modulus' => \pack('Ca*a*', 2, self::encodeLength(\strlen($modulus)), $modulus), + 'publicExponent' => \pack('Ca*a*', 2, self::encodeLength(\strlen($publicExponent)), $publicExponent) + ); + + $rsaPublicKey = \pack( + 'Ca*a*a*', + 48, + self::encodeLength(\strlen($components['modulus']) + \strlen($components['publicExponent'])), + $components['modulus'], + $components['publicExponent'] + ); + + // sequence(oid(1.2.840.113549.1.1.1), null)) = rsaEncryption. + $rsaOID = \pack('H*', '300d06092a864886f70d0101010500'); // hex version of MA0GCSqGSIb3DQEBAQUA + $rsaPublicKey = \chr(0) . $rsaPublicKey; + $rsaPublicKey = \chr(3) . self::encodeLength(\strlen($rsaPublicKey)) . $rsaPublicKey; + + $rsaPublicKey = \pack( + 'Ca*a*', + 48, + self::encodeLength(\strlen($rsaOID . $rsaPublicKey)), + $rsaOID . $rsaPublicKey + ); + + $rsaPublicKey = "-----BEGIN PUBLIC KEY-----\r\n" . + \chunk_split(\base64_encode($rsaPublicKey), 64) . + '-----END PUBLIC KEY-----'; + + return $rsaPublicKey; + } + + /** + * DER-encode the length + * + * DER supports lengths up to (2**8)**127, however, we'll only support lengths up to (2**8)**4. See + * {@link http://itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf#p=13 X.690 paragraph 8.1.3} for more information. + * + * @param int $length + * @return string + */ + private static function encodeLength($length) + { + if ($length <= 0x7F) { + return \chr($length); + } + + $temp = \ltrim(\pack('N', $length), \chr(0)); + + return \pack('Ca*', 0x80 | \strlen($temp), $temp); + } +} diff --git a/pfSense-pkg-API/files/etc/inc/php-jwt/src/JWT.php b/pfSense-pkg-API/files/etc/inc/php-jwt/src/JWT.php new file mode 100644 index 000000000..4860028bc --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/php-jwt/src/JWT.php @@ -0,0 +1,512 @@ + + * @author Anant Narayanan + * @license http://opensource.org/licenses/BSD-3-Clause 3-clause BSD + * @link https://github.com/firebase/php-jwt + */ +class JWT +{ + const ASN1_INTEGER = 0x02; + const ASN1_SEQUENCE = 0x10; + const ASN1_BIT_STRING = 0x03; + + /** + * When checking nbf, iat or expiration times, + * we want to provide some extra leeway time to + * account for clock skew. + */ + public static $leeway = 0; + + /** + * Allow the current timestamp to be specified. + * Useful for fixing a value within unit testing. + * + * Will default to PHP time() value if null. + */ + public static $timestamp = null; + + public static $supported_algs = array( + 'ES256' => array('openssl', 'SHA256'), + 'HS256' => array('hash_hmac', 'SHA256'), + 'HS384' => array('hash_hmac', 'SHA384'), + 'HS512' => array('hash_hmac', 'SHA512'), + 'RS256' => array('openssl', 'SHA256'), + 'RS384' => array('openssl', 'SHA384'), + 'RS512' => array('openssl', 'SHA512'), + ); + + /** + * Decodes a JWT string into a PHP object. + * + * @param string $jwt The JWT + * @param string|array|resource $key The key, or map of keys. + * If the algorithm used is asymmetric, this is the public key + * @param array $allowed_algs List of supported verification algorithms + * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' + * + * @return object The JWT's payload as a PHP object + * + * @throws UnexpectedValueException Provided JWT was invalid + * @throws SignatureInvalidException Provided JWT was invalid because the signature verification failed + * @throws BeforeValidException Provided JWT is trying to be used before it's eligible as defined by 'nbf' + * @throws BeforeValidException Provided JWT is trying to be used before it's been created as defined by 'iat' + * @throws ExpiredException Provided JWT has since expired, as defined by the 'exp' claim + * + * @uses jsonDecode + * @uses urlsafeB64Decode + */ + public static function decode($jwt, $key, array $allowed_algs = array()) + { + $timestamp = \is_null(static::$timestamp) ? \time() : static::$timestamp; + + if (empty($key)) { + throw new InvalidArgumentException('Key may not be empty'); + } + $tks = \explode('.', $jwt); + if (\count($tks) != 3) { + throw new UnexpectedValueException('Wrong number of segments'); + } + list($headb64, $bodyb64, $cryptob64) = $tks; + if (null === ($header = static::jsonDecode(static::urlsafeB64Decode($headb64)))) { + throw new UnexpectedValueException('Invalid header encoding'); + } + if (null === $payload = static::jsonDecode(static::urlsafeB64Decode($bodyb64))) { + throw new UnexpectedValueException('Invalid claims encoding'); + } + if (false === ($sig = static::urlsafeB64Decode($cryptob64))) { + throw new UnexpectedValueException('Invalid signature encoding'); + } + if (empty($header->alg)) { + throw new UnexpectedValueException('Empty algorithm'); + } + if (empty(static::$supported_algs[$header->alg])) { + throw new UnexpectedValueException('Algorithm not supported'); + } + if (!\in_array($header->alg, $allowed_algs)) { + throw new UnexpectedValueException('Algorithm not allowed'); + } + if ($header->alg === 'ES256') { + // OpenSSL expects an ASN.1 DER sequence for ES256 signatures + $sig = self::signatureToDER($sig); + } + + if (\is_array($key) || $key instanceof \ArrayAccess) { + if (isset($header->kid)) { + if (!isset($key[$header->kid])) { + throw new UnexpectedValueException('"kid" invalid, unable to lookup correct key'); + } + $key = $key[$header->kid]; + } else { + throw new UnexpectedValueException('"kid" empty, unable to lookup correct key'); + } + } + + // Check the signature + if (!static::verify("$headb64.$bodyb64", $sig, $key, $header->alg)) { + throw new SignatureInvalidException('Signature verification failed'); + } + + // Check the nbf if it is defined. This is the time that the + // token can actually be used. If it's not yet that time, abort. + if (isset($payload->nbf) && $payload->nbf > ($timestamp + static::$leeway)) { + throw new BeforeValidException( + 'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->nbf) + ); + } + + // Check that this token has been created before 'now'. This prevents + // using tokens that have been created for later use (and haven't + // correctly used the nbf claim). + if (isset($payload->iat) && $payload->iat > ($timestamp + static::$leeway)) { + throw new BeforeValidException( + 'Cannot handle token prior to ' . \date(DateTime::ISO8601, $payload->iat) + ); + } + + // Check if this token has expired. + if (isset($payload->exp) && ($timestamp - static::$leeway) >= $payload->exp) { + throw new ExpiredException('Expired token'); + } + + return $payload; + } + + /** + * Converts and signs a PHP object or array into a JWT string. + * + * @param object|array $payload PHP object or array + * @param string $key The secret key. + * If the algorithm used is asymmetric, this is the private key + * @param string $alg The signing algorithm. + * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' + * @param mixed $keyId + * @param array $head An array with header elements to attach + * + * @return string A signed JWT + * + * @uses jsonEncode + * @uses urlsafeB64Encode + */ + public static function encode($payload, $key, $alg = 'HS256', $keyId = null, $head = null) + { + $header = array('typ' => 'JWT', 'alg' => $alg); + if ($keyId !== null) { + $header['kid'] = $keyId; + } + if (isset($head) && \is_array($head)) { + $header = \array_merge($head, $header); + } + $segments = array(); + $segments[] = static::urlsafeB64Encode(static::jsonEncode($header)); + $segments[] = static::urlsafeB64Encode(static::jsonEncode($payload)); + $signing_input = \implode('.', $segments); + + $signature = static::sign($signing_input, $key, $alg); + $segments[] = static::urlsafeB64Encode($signature); + + return \implode('.', $segments); + } + + /** + * Sign a string with a given key and algorithm. + * + * @param string $msg The message to sign + * @param string|resource $key The secret key + * @param string $alg The signing algorithm. + * Supported algorithms are 'ES256', 'HS256', 'HS384', 'HS512', 'RS256', 'RS384', and 'RS512' + * + * @return string An encrypted message + * + * @throws DomainException Unsupported algorithm was specified + */ + public static function sign($msg, $key, $alg = 'HS256') + { + if (empty(static::$supported_algs[$alg])) { + throw new DomainException('Algorithm not supported'); + } + list($function, $algorithm) = static::$supported_algs[$alg]; + switch ($function) { + case 'hash_hmac': + return \hash_hmac($algorithm, $msg, $key, true); + case 'openssl': + $signature = ''; + $success = \openssl_sign($msg, $signature, $key, $algorithm); + if (!$success) { + throw new DomainException("OpenSSL unable to sign data"); + } else { + if ($alg === 'ES256') { + $signature = self::signatureFromDER($signature, 256); + } + return $signature; + } + } + } + + /** + * Verify a signature with the message, key and method. Not all methods + * are symmetric, so we must have a separate verify and sign method. + * + * @param string $msg The original message (header and body) + * @param string $signature The original signature + * @param string|resource $key For HS*, a string key works. for RS*, must be a resource of an openssl public key + * @param string $alg The algorithm + * + * @return bool + * + * @throws DomainException Invalid Algorithm or OpenSSL failure + */ + private static function verify($msg, $signature, $key, $alg) + { + if (empty(static::$supported_algs[$alg])) { + throw new DomainException('Algorithm not supported'); + } + + list($function, $algorithm) = static::$supported_algs[$alg]; + switch ($function) { + case 'openssl': + $success = \openssl_verify($msg, $signature, $key, $algorithm); + if ($success === 1) { + return true; + } elseif ($success === 0) { + return false; + } + // returns 1 on success, 0 on failure, -1 on error. + throw new DomainException( + 'OpenSSL error: ' . \openssl_error_string() + ); + case 'hash_hmac': + default: + $hash = \hash_hmac($algorithm, $msg, $key, true); + if (\function_exists('hash_equals')) { + return \hash_equals($signature, $hash); + } + $len = \min(static::safeStrlen($signature), static::safeStrlen($hash)); + + $status = 0; + for ($i = 0; $i < $len; $i++) { + $status |= (\ord($signature[$i]) ^ \ord($hash[$i])); + } + $status |= (static::safeStrlen($signature) ^ static::safeStrlen($hash)); + + return ($status === 0); + } + } + + /** + * Decode a JSON string into a PHP object. + * + * @param string $input JSON string + * + * @return object Object representation of JSON string + * + * @throws DomainException Provided string was invalid JSON + */ + public static function jsonDecode($input) + { + if (\version_compare(PHP_VERSION, '5.4.0', '>=') && !(\defined('JSON_C_VERSION') && PHP_INT_SIZE > 4)) { + /** In PHP >=5.4.0, json_decode() accepts an options parameter, that allows you + * to specify that large ints (like Steam Transaction IDs) should be treated as + * strings, rather than the PHP default behaviour of converting them to floats. + */ + $obj = \json_decode($input, false, 512, JSON_BIGINT_AS_STRING); + } else { + /** Not all servers will support that, however, so for older versions we must + * manually detect large ints in the JSON string and quote them (thus converting + *them to strings) before decoding, hence the preg_replace() call. + */ + $max_int_length = \strlen((string) PHP_INT_MAX) - 1; + $json_without_bigints = \preg_replace('/:\s*(-?\d{'.$max_int_length.',})/', ': "$1"', $input); + $obj = \json_decode($json_without_bigints); + } + + if ($errno = \json_last_error()) { + static::handleJsonError($errno); + } elseif ($obj === null && $input !== 'null') { + throw new DomainException('Null result with non-null input'); + } + return $obj; + } + + /** + * Encode a PHP object into a JSON string. + * + * @param object|array $input A PHP object or array + * + * @return string JSON representation of the PHP object or array + * + * @throws DomainException Provided object could not be encoded to valid JSON + */ + public static function jsonEncode($input) + { + $json = \json_encode($input); + if ($errno = \json_last_error()) { + static::handleJsonError($errno); + } elseif ($json === 'null' && $input !== null) { + throw new DomainException('Null result with non-null input'); + } + return $json; + } + + /** + * Decode a string with URL-safe Base64. + * + * @param string $input A Base64 encoded string + * + * @return string A decoded string + */ + public static function urlsafeB64Decode($input) + { + $remainder = \strlen($input) % 4; + if ($remainder) { + $padlen = 4 - $remainder; + $input .= \str_repeat('=', $padlen); + } + return \base64_decode(\strtr($input, '-_', '+/')); + } + + /** + * Encode a string with URL-safe Base64. + * + * @param string $input The string you want encoded + * + * @return string The base64 encode of what you passed in + */ + public static function urlsafeB64Encode($input) + { + return \str_replace('=', '', \strtr(\base64_encode($input), '+/', '-_')); + } + + /** + * Helper method to create a JSON error. + * + * @param int $errno An error number from json_last_error() + * + * @return void + */ + private static function handleJsonError($errno) + { + $messages = array( + JSON_ERROR_DEPTH => 'Maximum stack depth exceeded', + JSON_ERROR_STATE_MISMATCH => 'Invalid or malformed JSON', + JSON_ERROR_CTRL_CHAR => 'Unexpected control character found', + JSON_ERROR_SYNTAX => 'Syntax error, malformed JSON', + JSON_ERROR_UTF8 => 'Malformed UTF-8 characters' //PHP >= 5.3.3 + ); + throw new DomainException( + isset($messages[$errno]) + ? $messages[$errno] + : 'Unknown JSON error: ' . $errno + ); + } + + /** + * Get the number of bytes in cryptographic strings. + * + * @param string $str + * + * @return int + */ + private static function safeStrlen($str) + { + if (\function_exists('mb_strlen')) { + return \mb_strlen($str, '8bit'); + } + return \strlen($str); + } + + /** + * Convert an ECDSA signature to an ASN.1 DER sequence + * + * @param string $sig The ECDSA signature to convert + * @return string The encoded DER object + */ + private static function signatureToDER($sig) + { + // Separate the signature into r-value and s-value + list($r, $s) = \str_split($sig, (int) (\strlen($sig) / 2)); + + // Trim leading zeros + $r = \ltrim($r, "\x00"); + $s = \ltrim($s, "\x00"); + + // Convert r-value and s-value from unsigned big-endian integers to + // signed two's complement + if (\ord($r[0]) > 0x7f) { + $r = "\x00" . $r; + } + if (\ord($s[0]) > 0x7f) { + $s = "\x00" . $s; + } + + return self::encodeDER( + self::ASN1_SEQUENCE, + self::encodeDER(self::ASN1_INTEGER, $r) . + self::encodeDER(self::ASN1_INTEGER, $s) + ); + } + + /** + * Encodes a value into a DER object. + * + * @param int $type DER tag + * @param string $value the value to encode + * @return string the encoded object + */ + private static function encodeDER($type, $value) + { + $tag_header = 0; + if ($type === self::ASN1_SEQUENCE) { + $tag_header |= 0x20; + } + + // Type + $der = \chr($tag_header | $type); + + // Length + $der .= \chr(\strlen($value)); + + return $der . $value; + } + + /** + * Encodes signature from a DER object. + * + * @param string $der binary signature in DER format + * @param int $keySize the number of bits in the key + * @return string the signature + */ + private static function signatureFromDER($der, $keySize) + { + // OpenSSL returns the ECDSA signatures as a binary ASN.1 DER SEQUENCE + list($offset, $_) = self::readDER($der); + list($offset, $r) = self::readDER($der, $offset); + list($offset, $s) = self::readDER($der, $offset); + + // Convert r-value and s-value from signed two's compliment to unsigned + // big-endian integers + $r = \ltrim($r, "\x00"); + $s = \ltrim($s, "\x00"); + + // Pad out r and s so that they are $keySize bits long + $r = \str_pad($r, $keySize / 8, "\x00", STR_PAD_LEFT); + $s = \str_pad($s, $keySize / 8, "\x00", STR_PAD_LEFT); + + return $r . $s; + } + + /** + * Reads binary DER-encoded data and decodes into a single object + * + * @param string $der the binary data in DER format + * @param int $offset the offset of the data stream containing the object + * to decode + * @return array [$offset, $data] the new offset and the decoded object + */ + private static function readDER($der, $offset = 0) + { + $pos = $offset; + $size = \strlen($der); + $constructed = (\ord($der[$pos]) >> 5) & 0x01; + $type = \ord($der[$pos++]) & 0x1f; + + // Length + $len = \ord($der[$pos++]); + if ($len & 0x80) { + $n = $len & 0x1f; + $len = 0; + while ($n-- && $pos < $size) { + $len = ($len << 8) | \ord($der[$pos++]); + } + } + + // Value + if ($type == self::ASN1_BIT_STRING) { + $pos++; // Skip the first contents octet (padding indicator) + $data = \substr($der, $pos, $len - 1); + $pos += $len - 1; + } elseif (!$constructed) { + $data = \substr($der, $pos, $len); + $pos += $len; + } else { + $data = null; + } + + return array($pos, $data); + } +} diff --git a/pfSense-pkg-API/files/etc/inc/php-jwt/src/SignatureInvalidException.php b/pfSense-pkg-API/files/etc/inc/php-jwt/src/SignatureInvalidException.php new file mode 100644 index 000000000..87cb34df7 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/php-jwt/src/SignatureInvalidException.php @@ -0,0 +1,6 @@ + any json + + 3600 sha256 16 diff --git a/pfSense-pkg-API/files/usr/local/www/api/index.php b/pfSense-pkg-API/files/usr/local/www/api/index.php index c3cf44054..a18412862 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/index.php @@ -16,7 +16,7 @@ $pkg_config = get_api_configuration(); // Save our entire pkg config $pkg_index = $pkg_config[0]; // Save our pkg configurations index value $api_config = $pkg_config[1]; // Save our api configuration from our pkg config -$available_auth_modes = array("local" => "Local Database", "base64" => "Base64", "token" => "API Token"); +$available_auth_modes = array("local" => "Local Database", "base64" => "Base64", "token" => "API Token", "jwt" => "JWT"); $available_hash_algos = array("sha256" => "SHA256", "sha384" => "SHA384", "sha512" => "SHA512", "md5" => "MD5"); $available_key_bytes = array("16", "32", "64"); // Save our allowed key bitlengths $non_config_ifs = array("any" => "Any", "localhost" => "Link-local"); // Save non-configurable interface ids @@ -27,6 +27,12 @@ $new_key = api_generate_token($user); print_apply_result_box(0, "\nSave this API key somewhere safe, it cannot be viewed again: \n".$new_key); } +// Rotate JWT server key requested +if ($_POST["rotate_server_key"] === "1") { + api_create_jwt_server_key(true); + print_apply_result_box(0, "\nRotated JWT server key.\n"); +} + if (isset($_POST["del"]) and is_numeric($_POST["del"])) { $del_key = $_POST["del"]; unset($config["installedpackages"]["package"][$pkg_index]["conf"]["keys"]["key"][$del_key]); @@ -49,6 +55,10 @@ if (isset($_POST["authmode"])) { $api_config["authmode"] = $_POST["authmode"]; } + // Save JWT expiration value to coonfig + if (isset($_POST["jwt_exp"])) { + $api_config["jwt_exp"] = $_POST["jwt_exp"]; + } // Save key hash algos to config if (isset($_POST["keyhash"])) { $api_config["keyhash"] = $_POST["keyhash"]; @@ -129,45 +139,7 @@ } ?> - Authentication method API uses to authenticate during API calls. `Local Database` uses basic authentication using your pfSense user/password, `Base 64` uses base64 encoded pfSense user/password, `API Token` generates specific API tokens for API access - - -
- -
- - Hashing algorithm used to store API keys -
-
-
- -
- - Bit strength used when generating API keys + Authentication method API uses to authenticate during API calls. `Local Database` uses basic authentication using your pfSense user/password, `Base 64` uses base64 encoded pfSense user/password, `API Token` generates specific API tokens for API access. `JWT` allows user to obtain a token via access token endpoint..
@@ -186,14 +158,69 @@
-
- - ".PHP_EOL; + echo " ".PHP_EOL; + echo "
".PHP_EOL; + echo " ".PHP_EOL; + echo " How long (in seconds) the JWT is valid for. Allows a minimum is 600 seconds (5 minutes) and maximum of 86400 seconds (1 day).".PHP_EOL; + echo "
".PHP_EOL; + echo " ".PHP_EOL; + echo " ".PHP_EOL; + echo " ".PHP_EOL; + } elseif ($api_config["authmode"] === "token") { + echo "
".PHP_EOL; + echo " ".PHP_EOL; + echo "
".PHP_EOL; + echo " ".PHP_EOL; + echo " Hashing algorithm used to store API keys".PHP_EOL; + echo "
".PHP_EOL; + echo "
".PHP_EOL; + echo "
".PHP_EOL; + echo " ".PHP_EOL; + echo "
".PHP_EOL; + echo " ".PHP_EOL; + echo " Bit strength used when generating API keys".PHP_EOL; + echo "
".PHP_EOL; + echo "
".PHP_EOL; + echo " ".PHP_EOL; + } else { + echo " ".PHP_EOL; + } + ?> + + + + +".PHP_EOL; echo "
".PHP_EOL; echo "

API Credentials

".PHP_EOL; @@ -227,17 +254,11 @@ echo "
".PHP_EOL; echo " ".PHP_EOL; echo "".PHP_EOL; - } - ?> - "; echo " "; echo " "; echo " Generate "; echo ""; } - ?> - \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php new file mode 100644 index 000000000..7582a757a --- /dev/null +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php @@ -0,0 +1,10 @@ + Date: Thu, 20 Aug 2020 15:35:31 -0600 Subject: [PATCH 04/16] Adding concept of object oriented structure and microframework --- .../files/etc/inc/api/APIResponse.inc | 73 ++++++++++ .../files/etc/inc/api/APITools.inc | 126 +++++++++++++++++ .../etc/inc/api/api_calls/APIAccessToken.inc | 81 +++++++++++ .../files/etc/inc/api/framework/APIAuth.inc | 128 ++++++++++++++++++ .../etc/inc/api/framework/APIBaseModel.inc | 59 ++++++++ .../usr/local/share/pfSense-pkg-API/info.xml | 1 + 6 files changed, 468 insertions(+) create mode 100644 pfSense-pkg-API/files/etc/inc/api/APIResponse.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/APITools.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc diff --git a/pfSense-pkg-API/files/etc/inc/api/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/APIResponse.inc new file mode 100644 index 000000000..97ad06594 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/APIResponse.inc @@ -0,0 +1,73 @@ + [ + "status" => "ok", + "code" => 200, + "return" => $id, + "message" => "Success", + ], + 1 => [ + "status" => "server error", + "code" => 500, + "return" => $id, + "message" => "Process encountered unexpected error", + ], + 2 => [ + "status" => "method not allowed", + "code" => 405, + "return" => $id, + "message" => "Invalid HTTP method", + ], + 3 => [ + "status" => "unauthorized", + "code" => 401, + "return" => $id, + "message" => "Authentication failed", + ], + 4 => [ + "status" => "forbidden", + "code" => 401, + "return" => $id, + "message" => "Authorization failed", + ], + 5 => [ + "status" => "not implemented", + "code" => 501, + "return" => $id, + "message" => "Incompatible pfSense version", + ], + 6 => [ + "status" => "forbidden", + "code" => 403, + "return" => $id, + "message" => "Requested action is not allowed", + ], + 7 => [ + "status" => "not found", + "code" => 404, + "return" => $id, + "message" => "Search attribute not found", + ], + 8 => [ + "status" => "not found", + "code" => 404, + "return" => $id, + "message" => "Could not identify pfSense version", + ], + 9 => [ + "status" => "forbidden", + "code" => 403, + "return" => $id, + "message" => "Authentication mode must be set to JWT to enable access token authentication", + ], + ]; + + $response = $responses[$id]; + $response["data"] = $data; + return $response; +} diff --git a/pfSense-pkg-API/files/etc/inc/api/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/APITools.inc new file mode 100644 index 000000000..33cc3e5e7 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/APITools.inc @@ -0,0 +1,126 @@ + $pkg) { + if ($pkg["name"] === $api_pkg_name) { + return array($id, $pkg["conf"]); + } + } + } +} + +# Checks if a specified user is disabled +function is_user_disabled($username) { + global $config; + $users = index_users(); + if (array_key_exists("disabled", $config["system"]["user"][$users[$username]])) { + return true; + } + return false; +} + +# Creates JWT server key if one does not exist, or optionally allows rotation of the JWT server key +function create_jwt_server_key($rotate=false) { + global $config; + $pkg_index = get_api_configuration()[0]; // Save our current API configs pkg index + $api_config = get_api_configuration()[1]; // Save our current API config + # Create a new server key if one is not set + if (empty($api_config["server_key"]) or $rotate === true) { + $config["installedpackages"]["package"][$pkg_index]["conf"]["server_key"] = bin2hex(random_bytes(32)); + write_config(); + } +} + +# Creates a JWT to use for JWT authentication +function create_jwt($data) { + global $config; + $api_config = get_api_config()[1]; // Save our current API config + $token_exp = $api_config["jwt_exp"]; // Expire token in one hours + create_jwt_server_key(); // Ensure we have a JWT server key + $payload = array( + "iss" => $config["system"]["hostname"], + "exp" => time() + $token_exp, + "nbf" => time(), + "data" => $data + ); + return JWT::encode($payload, $api_config["server_key"]); +} + +# Decodes a JWT using our store server key +function decode_jwt($token) { + $key = get_api_config()[1]["server_key"]; // Save our current server key + try { + $decoded = (array) JWT::decode($token, $key, array('HS256')); + } catch (Exception $e) { + $decoded = false; + } + return $decoded; +} + +# Get our API tokens for a given username +function get_existing_tokens($username) { + // Local variables + $api_config = get_api_config()[1]; + $key_user = bin2hex($username); // Save our user's dedicated API client-ID + $user_keys = []; + foreach ($api_config["keys"]["key"] as $id => $key) { + if ($key["client_id"] === $key_user) { + $user_keys[$id] = array("client_token" => $key["client_token"], "algo" => $key["algo"]); + } + } + return $user_keys; +} + +# Authenticate using an API token +function authenticate_token($cid, $ctoken) { + $authenticated = false; + $hex_to_user = pack("H*", $cid); + // First check if our hex decoded user exists + if (in_array($hex_to_user, index_users())) { + // Loop through each of our users API tokens and check if key matches + foreach (get_existing_tokens($hex_to_user) as $id => $data) { + $hash_input_key = hash($data["algo"], $ctoken); // Hash our key using our configured algos + if ($hash_input_key === $data["client_token"]) { + $authenticated = true; + break; + } + } + } + return $authenticated; +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc b/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc new file mode 100644 index 000000000..8d781cb80 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc @@ -0,0 +1,81 @@ +req_privs = []; + $this->methods = ["GET"]; + $this->client = new APIAuth($this->req_privs); + $this->errors = []; + $this->valid_data = []; + } + + # Validate our API configurations auth mode (must be JWT) + private function validateAuthMode() { + $api_config = APITools\get_api_config()[1]; + + # Add error if our auth mode is invalid + if ($api_config["authmode"] !== "jwt") { + $this->errors[] = APIResponse\get(9); + } + } + + # Validate our request + public function validate($validate_auth=true, $validate_http_method=true) { + # Validate authentication + if ($validate_auth === true) { + # Add error if user is not authenticated + if (!$this->client->is_authenticated) { + $this->errors[] = APIResponse\get(3); + } + # Add error if user is not authorized + if (!$this->client->is_authorized) { + $this->errors[] = APIResponse\get(4); + } + } + + # Validate HTTP method + if ($validate_http_method === true) { + + } + + # Run our field/conditional validators + $this->validateAuthMode(); + + # Check if we have errors in our error array + if (count($this->errors) === 0) { + return true; + } else { + return false; + } + } + + # Run our call. This method will return an assoc array containing the API response results + public function call() { + # Check if our request is valid + if ($this->validate()) { + $jwt = api_create_jwt($this->client->username); + return ApiResponse\get(0, [["token" => $jwt]]); + } else { + return $this->errors[0]; + } + } + + # Listen for client requests. This method should executed on the API endpoint. + public function listen() { + # RUN API CALL + $resp = $this->call(); + http_response_code($resp["code"]); + echo json_encode($resp) . PHP_EOL; + exit(); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc new file mode 100644 index 000000000..da16f466e --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc @@ -0,0 +1,128 @@ +api_config = APITools\get_api_config()[1]; + $this->auth_mode = $this->api_config["authmode"]; + $this->request = APITools\get_request_data(); + $this->req_privs = $req_privs; + $this->privs = []; + $this->is_authenticated = $this->authenticate(); + $this->is_authorized = $this->authorize(); + } + + # AUTHENTICATION # + # Attempts to authenticate using local database authentication + private function authenticateLocalDatabase() { + $this->username = $this->request["client-id"]; + var_dump(authenticate_user("admin", "pfsense")); + // Authenticate against local database + if (authenticate_user($this->username, $this->request["client-token"])) { + // Ensure user is not disabled + if (APITools\is_user_disabled($this->username) === false) { + unset($_SESSION["Username"]); + $_SESSION["Username"] = $this->username; + return true; + } + } + return false; + } + + # Attempts to authenticate using JWT authentication + private function authenticateJWT() { + $auth_header = explode(" ", $_SERVER["HTTP_AUTHORIZATION"]); + $token_type = $auth_header[0]; + $token = $auth_header[1]; + $decoded_jwt = APITools\decode_jwt($token); + + // Check that the JWT from our Authorization header is valid + if ($token_type === "Bearer" and $decoded_jwt !== false) { + $this->username = $decoded_jwt["data"]; + // Ensure user is not disabled + if (APITools\is_user_disabled($this->username) === false) { + unset($_SESSION["Username"]); + $_SESSION["Username"] = $this->username; + return true; + } + } + return false; + } + + # Attempts to authenticate using API token authentication + private function authenticateToken() { + if (APITools\authenticate_token($this->request["client-id"], $this->request["client-id"]) === true) { + $this->username = pack("H*", $this->request["client-id"]); + // Ensure user is not disabled + if (APITools\is_user_disabled($this->request["client-id"]) === false) { + unset($_SESSION["Username"]); + $_SESSION["Username"] = $this->username; + return true; + } + } + return false; + } + + # Chooses correct auth method based on configured auth mode. Returns bool. + public function authenticate() { + # Attempt to authenticate + if ($this->auth_mode === "local") { + $resp = $this->authenticateLocalDatabase(); + } + elseif ($this->auth_mode === "jwt") { + $resp = $this->authenticateJWT(); + } + elseif ($this->auth_mode === "token") { + $resp = $this->authenticateToken(); + } + else { + $resp = false; + } + + # Set our class is_authenticated attribute to our authentication resp and return the resp + $this->is_authenticated = $resp; + return $this->is_authenticated; + } + + # AUTHORIZATION # + # Checks if the current user has the necessary privileges to access this resource + public function authorize() { + // Local variables + $authorized = false; + $client_config =& getUserEntry($this->username);; + $this->privs = get_user_privileges($client_config); + $read_only = array_key_exists("readonly", $this->api_config); + + # If no require privileges were given, assume call is always authorized + if (!empty($this->req_privs)) { + // Ensure our API is not read only OR if it is read only, only authorize the request if it is a GET request + if ($read_only === false or ($read_only === true and $_SERVER['REQUEST_METHOD'] === "GET")) { + // Loop through each of our req privs and ensure the client has them, also check if access is read only + foreach ($this->req_privs as &$priv) { + if (in_array($priv, $this->privs)) { + $authorized = true; + break; + } + } + } + } else { + $authorized = true; + } + + # Set our class is_authorized attribute to our authorization resp and return the resp + $this->is_authorized = $authorized; + return $authorized; + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc new file mode 100644 index 000000000..e45a07c4d --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc @@ -0,0 +1,59 @@ +methods = ["POST"]; + $this->privileges = ["pages-all"]; + $this->client = new APIAuth($this->privileges); + $this->requires_auth = true; + $this->validators = []; + $this->initial_data = APITools\get_request_data(); + $this->validated_data = []; + $this->errors = []; + + } + + # Validate our request + public function validate() { + # Validate authentication + if ($this->requires_auth === true) { + # Add error if user is not authenticated + if (!$this->client->is_authenticated) { + $this->errors[] = APIResponse\get(3); + } + # Add error if user is not authorized + if (!$this->client->is_authorized) { + $this->errors[] = APIResponse\get(4); + } + } + + # Validate HTTP method + if (!in_array($_SERVER["REQUEST_METHOD"], $this->methods)) { + + } + + # Run our field/conditional validators + $this->validateAuthMode(); + + # Check if we have errors in our error array + if (count($this->errors) === 0) { + return true; + } else { + return false; + } + } + +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/share/pfSense-pkg-API/info.xml b/pfSense-pkg-API/files/usr/local/share/pfSense-pkg-API/info.xml index 347deec8a..7452b1b33 100644 --- a/pfSense-pkg-API/files/usr/local/share/pfSense-pkg-API/info.xml +++ b/pfSense-pkg-API/files/usr/local/share/pfSense-pkg-API/info.xml @@ -12,6 +12,7 @@ any + local json 3600 From be5e7a9e1bc6c6780628fb68c20d82a85a3c8c1d Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Thu, 20 Aug 2020 21:08:07 -0600 Subject: [PATCH 05/16] Working on OOP framework for API models --- .../etc/inc/api/api_calls/APIAccessToken.inc | 73 +++---------------- .../files/etc/inc/api/framework/APIAuth.inc | 15 ++-- .../etc/inc/api/framework/APIBaseModel.inc | 63 +++++++++++----- .../inc/api/{ => framework}/APIResponse.inc | 0 .../etc/inc/api/{ => framework}/APITools.inc | 0 5 files changed, 63 insertions(+), 88 deletions(-) rename pfSense-pkg-API/files/etc/inc/api/{ => framework}/APIResponse.inc (100%) rename pfSense-pkg-API/files/etc/inc/api/{ => framework}/APITools.inc (100%) diff --git a/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc b/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc index 8d781cb80..acf9b4fcd 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc @@ -1,22 +1,15 @@ req_privs = []; + parent::__construct(); $this->methods = ["GET"]; - $this->client = new APIAuth($this->req_privs); - $this->errors = []; - $this->valid_data = []; + $this->validators = ["validateAuthMode"]; } # Validate our API configurations auth mode (must be JWT) @@ -29,53 +22,9 @@ class APIAccessToken { } } - # Validate our request - public function validate($validate_auth=true, $validate_http_method=true) { - # Validate authentication - if ($validate_auth === true) { - # Add error if user is not authenticated - if (!$this->client->is_authenticated) { - $this->errors[] = APIResponse\get(3); - } - # Add error if user is not authorized - if (!$this->client->is_authorized) { - $this->errors[] = APIResponse\get(4); - } - } - - # Validate HTTP method - if ($validate_http_method === true) { - - } - - # Run our field/conditional validators - $this->validateAuthMode(); - - # Check if we have errors in our error array - if (count($this->errors) === 0) { - return true; - } else { - return false; - } - } - - # Run our call. This method will return an assoc array containing the API response results - public function call() { - # Check if our request is valid - if ($this->validate()) { - $jwt = api_create_jwt($this->client->username); - return ApiResponse\get(0, [["token" => $jwt]]); - } else { - return $this->errors[0]; - } - } - - # Listen for client requests. This method should executed on the API endpoint. - public function listen() { - # RUN API CALL - $resp = $this->call(); - http_response_code($resp["code"]); - echo json_encode($resp) . PHP_EOL; - exit(); + # Override action subclass to create a JWT and return it to the user + public function action() { + $jwt = api_create_jwt($this->client->username); + return APIResponse\get(0, ["token" => $jwt]); } } \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc index da16f466e..d284b3698 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc @@ -1,5 +1,5 @@ username = $this->request["client-id"]; - var_dump(authenticate_user("admin", "pfsense")); // Authenticate against local database if (authenticate_user($this->username, $this->request["client-token"])) { // Ensure user is not disabled @@ -42,7 +41,7 @@ class APIAuth { } # Attempts to authenticate using JWT authentication - private function authenticateJWT() { + private function authenticate_jwt() { $auth_header = explode(" ", $_SERVER["HTTP_AUTHORIZATION"]); $token_type = $auth_header[0]; $token = $auth_header[1]; @@ -62,7 +61,7 @@ class APIAuth { } # Attempts to authenticate using API token authentication - private function authenticateToken() { + private function authenticate_token() { if (APITools\authenticate_token($this->request["client-id"], $this->request["client-id"]) === true) { $this->username = pack("H*", $this->request["client-id"]); // Ensure user is not disabled @@ -79,13 +78,13 @@ class APIAuth { public function authenticate() { # Attempt to authenticate if ($this->auth_mode === "local") { - $resp = $this->authenticateLocalDatabase(); + $resp = $this->authenticate_local_database(); } elseif ($this->auth_mode === "jwt") { - $resp = $this->authenticateJWT(); + $resp = $this->authenticate_jwt(); } elseif ($this->auth_mode === "token") { - $resp = $this->authenticateToken(); + $resp = $this->authenticate_token(); } else { $resp = false; diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc index e45a07c4d..660c8ce49 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc @@ -1,7 +1,7 @@ methods = ["POST"]; - $this->privileges = ["pages-all"]; + $this->methods = ["GET", "POST"]; + $this->privileges = ["page-all"]; $this->client = new APIAuth($this->privileges); $this->requires_auth = true; $this->validators = []; @@ -26,29 +25,42 @@ class APIBaseModel { } - # Validate our request - public function validate() { - # Validate authentication + private function check_authentication() { if ($this->requires_auth === true) { - # Add error if user is not authenticated if (!$this->client->is_authenticated) { $this->errors[] = APIResponse\get(3); } - # Add error if user is not authorized - if (!$this->client->is_authorized) { - $this->errors[] = APIResponse\get(4); - } } + } - # Validate HTTP method + private function check_authorization() { + if (!$this->client->is_authorized) { + $this->errors[] = APIResponse\get(4); + } + } + + private function check_method() { if (!in_array($_SERVER["REQUEST_METHOD"], $this->methods)) { + $this->errors[] = APIResponse\get(2); + } + } + public function action() { + # This function is intended to be overridden by an API model extended class + # Any configuration writes, system configurations, etc should be added when overriding this base class + # If this class is not overridden a 500 unexpected error is returned + return APIResponse\get(1); + } + + public function validate() { + $this->check_method(); + if ($this->requires_auth) { + $this->check_authentication(); + $this->check_authorization(); } + $this->errors = array_merge($this->errors, $this->validators); - # Run our field/conditional validators - $this->validateAuthMode(); - # Check if we have errors in our error array if (count($this->errors) === 0) { return true; } else { @@ -56,4 +68,19 @@ class APIBaseModel { } } + public function call() { + if ($this->validate()) { + return $this->action(); + } else { + return $this->errors[0]; + } + } + + public function listen() { + $resp = $this->call(); + http_response_code($resp["code"]); + echo json_encode($resp) . PHP_EOL; + exit(); + } + } \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc similarity index 100% rename from pfSense-pkg-API/files/etc/inc/api/APIResponse.inc rename to pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc diff --git a/pfSense-pkg-API/files/etc/inc/api/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc similarity index 100% rename from pfSense-pkg-API/files/etc/inc/api/APITools.inc rename to pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc From 4db2f265f4fa636c65d0f1ea36a56e3740d9eef5 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Fri, 21 Aug 2020 09:10:32 -0600 Subject: [PATCH 06/16] Created API model for our access_token endpoint --- pfSense-pkg-API/files/etc/inc/api.inc | 1 + .../etc/inc/api/api_calls/APIAccessToken.inc | 13 +-- .../files/etc/inc/api/framework/APIAuth.inc | 5 +- .../etc/inc/api/framework/APIBaseModel.inc | 97 ++++++++++++++----- .../files/etc/inc/api/framework/APITools.inc | 63 +++++++++++- .../files/usr/local/www/api/index.php | 13 ++- .../local/www/api/v1/access_token/index.php | 7 +- 7 files changed, 153 insertions(+), 46 deletions(-) diff --git a/pfSense-pkg-API/files/etc/inc/api.inc b/pfSense-pkg-API/files/etc/inc/api.inc index 2238caf55..ae8a71c95 100644 --- a/pfSense-pkg-API/files/etc/inc/api.inc +++ b/pfSense-pkg-API/files/etc/inc/api.inc @@ -352,6 +352,7 @@ function api_whitelist_check() { $if_conf = $config["interfaces"]; $whitelist = array("any"); $srv_ip = $_SERVER["SERVER_ADDR"]; + // Check that we have a package configuration if (is_numeric($pkg_id)) { // Override defaults with user specified config diff --git a/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc b/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc index acf9b4fcd..abfa66458 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc @@ -1,15 +1,16 @@ methods = ["GET"]; - $this->validators = ["validateAuthMode"]; + $this->set_auth_mode = "local"; + $this->methods = ["POST"]; + $this->validators = [ + $this->validateAuthMode() + ]; } # Validate our API configurations auth mode (must be JWT) @@ -18,13 +19,13 @@ class APIAccessToken extends APIBaseModel { # Add error if our auth mode is invalid if ($api_config["authmode"] !== "jwt") { - $this->errors[] = APIResponse\get(9); + return APIResponse\get(9); } } # Override action subclass to create a JWT and return it to the user public function action() { - $jwt = api_create_jwt($this->client->username); + $jwt = APITools\create_jwt($this->client->username); return APIResponse\get(0, ["token" => $jwt]); } } \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc index d284b3698..5fde932f9 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc @@ -1,7 +1,6 @@ api_config = APITools\get_api_config()[1]; - $this->auth_mode = $this->api_config["authmode"]; + $this->auth_mode = (is_null($enforce_auth_mode)) ? $this->api_config["authmode"] : $enforce_auth_mode; $this->request = APITools\get_request_data(); $this->req_privs = $req_privs; $this->privs = []; diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc index 660c8ce49..01add862a 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc @@ -12,12 +12,14 @@ class APIBaseModel { public $errors; public $methods; public $requires_auth; + public $set_auth_mode; public function __construct() { $this->methods = ["GET", "POST"]; $this->privileges = ["page-all"]; - $this->client = new APIAuth($this->privileges); + $this->client = null; $this->requires_auth = true; + $this->set_auth_mode = null; $this->validators = []; $this->initial_data = APITools\get_request_data(); $this->validated_data = []; @@ -25,26 +27,6 @@ class APIBaseModel { } - private function check_authentication() { - if ($this->requires_auth === true) { - if (!$this->client->is_authenticated) { - $this->errors[] = APIResponse\get(3); - } - } - } - - private function check_authorization() { - if (!$this->client->is_authorized) { - $this->errors[] = APIResponse\get(4); - } - } - - private function check_method() { - if (!in_array($_SERVER["REQUEST_METHOD"], $this->methods)) { - $this->errors[] = APIResponse\get(2); - } - } - public function action() { # This function is intended to be overridden by an API model extended class # Any configuration writes, system configurations, etc should be added when overriding this base class @@ -53,12 +35,15 @@ class APIBaseModel { } public function validate() { + $this->check_enable(); + $this->check_server_ip(); + $this->check_version(); $this->check_method(); if ($this->requires_auth) { $this->check_authentication(); $this->check_authorization(); } - $this->errors = array_merge($this->errors, $this->validators); + $this->errors = array_filter(array_merge($this->errors, $this->validators)); if (count($this->errors) === 0) { @@ -77,10 +62,78 @@ class APIBaseModel { } public function listen() { + header("Content-Type: application/json", true); + header("Referer: no-referrer"); $resp = $this->call(); http_response_code($resp["code"]); echo json_encode($resp) . PHP_EOL; exit(); } + private function check_authentication() { + $this->client = new APIAuth($this->privileges, $this->set_auth_mode); + if ($this->requires_auth === true) { + if (!$this->client->is_authenticated) { + $this->errors[] = APIResponse\get(3); + } + } + } + + private function check_authorization() { + if (!$this->client->is_authorized) { + $this->errors[] = APIResponse\get(4); + } + } + + private function check_method() { + if (!in_array($_SERVER["REQUEST_METHOD"], $this->methods)) { + $this->errors[] = APIResponse\get(2); + } + } + + # Check if the API is enabled before answering calls, if not, redirect to wc login + private function check_enable() { + $api_config = APITools\get_api_config()[1]; + if (!isset($api_config["enable"])) { + header("Location: /"); + die(); + } + } + + # Check if server is running a supported version of pfSense + private function check_version() { + # Local variables + $curr_ver = str_replace(".", "", explode("-", APITools\get_pfsense_version()["version"])[0]); + $min_ver = 244; + $curr_ver = is_numeric($curr_ver) ? intval($curr_ver) : 0; + if ($curr_ver < $min_ver) { + $this->errors[] = APIResponse\get(5); + } + } + + # Check if server IP is allowed to answer API calls. Redirects to login if not + private function check_server_ip() { + $pkg_conf = APITools\get_api_config()[1]; + $allow_ifs = $pkg_conf["allowed_interfaces"]; + $whitelist = explode(",", $allow_ifs); + + // Check if our server IP is in our whitelist + foreach ($whitelist as $wif) { + $if_info = get_interface_info($wif); + // Check if our server IP is a valid if address, localhost, or any + if ($_SERVER["SERVER_ADDR"] === $if_info["ipaddr"]) { + return; + } elseif ($_SERVER["SERVER_ADDR"] === $if_info["ipaddrv6"]) { + return; + } elseif (in_array($_SERVER["SERVER_ADDR"], ["::1", "127.0.0.1", "localhost"]) and $wif === "localhost") { + return; + }elseif ($wif === "any") { + return; + } + } + + # Return 444 response if we did not find a previous match + $this->errors[] = APIResponse\get(6); + } + } \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index 33cc3e5e7..84a448afc 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -1,6 +1,5 @@ []) : $api_config["keys"]; + $api_config["keys"]["key"][] = array("client_id" => $key_user, "client_token" => $key_hash, "algo" => $key_hash_algo); + + // Write our changes + $config["installedpackages"]["package"][$pkg_index]["conf"] = $api_config; // Write change to config + $change_note = " Generated API key"; // Add a change note + write_config(sprintf(gettext($change_note))); // Apply our configuration change + return $key_new; } \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/index.php b/pfSense-pkg-API/files/usr/local/www/api/index.php index a18412862..8ab084e92 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/index.php @@ -1,7 +1,7 @@ "Local Database", "base64" => "Base64", "token" => "API Token", "jwt" => "JWT"); +$available_auth_modes = array("local" => "Local Database", "token" => "API Token", "jwt" => "JWT"); $available_hash_algos = array("sha256" => "SHA256", "sha384" => "SHA384", "sha512" => "SHA512", "md5" => "MD5"); $available_key_bytes = array("16", "32", "64"); // Save our allowed key bitlengths $non_config_ifs = array("any" => "Any", "localhost" => "Link-local"); // Save non-configurable interface ids @@ -24,12 +23,12 @@ // UPON POST if ($_POST["gen"] === "1") { - $new_key = api_generate_token($user); + $new_key = APITools\generate_token($user); print_apply_result_box(0, "\nSave this API key somewhere safe, it cannot be viewed again: \n".$new_key); } // Rotate JWT server key requested if ($_POST["rotate_server_key"] === "1") { - api_create_jwt_server_key(true); + APITools\create_jwt_server_key(true); print_apply_result_box(0, "\nRotated JWT server key.\n"); } @@ -220,7 +219,7 @@ ".PHP_EOL; echo "
".PHP_EOL; echo "

API Credentials

".PHP_EOL; diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php index 7582a757a..84237de5c 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php @@ -1,10 +1,7 @@ listen(); From 96ef7e49b7fab2dcaa40e0c6fa4216524feaeac9 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Fri, 21 Aug 2020 21:08:06 -0600 Subject: [PATCH 07/16] Updated contribution doc, started painful migration from function based API calls to object oriented design, small framework adjustments --- CONTRIBUTING.md | 314 ++++++++----- pfSense-pkg-API/Makefile | 432 +----------------- pfSense-pkg-API/files/etc/inc/api.inc | 2 +- .../APIAccessToken.inc | 2 +- .../etc/inc/api/api_models/APIFirewallNat.inc | 22 + .../etc/inc/api/api_models/APIStatusCarp.inc | 23 + .../api/api_models/APIStatusCarpModify.inc | 45 ++ .../files/etc/inc/api/framework/APIAuth.inc | 2 +- .../etc/inc/api/framework/APIBaseModel.inc | 2 +- .../etc/inc/api/framework/APIResponse.inc | 6 + .../files/etc/inc/api/framework/APITools.inc | 80 +++- pfSense-pkg-API/files/etc/inc/apicalls.inc | 2 +- .../local/www/api/v1/access_token/index.php | 2 +- .../local/www/api/v1/firewall/nat/index.php | 9 +- .../local/www/api/v1/status/carp/index.php | 9 +- .../www/api/v1/status/carp/modify/index.php | 9 +- 16 files changed, 398 insertions(+), 563 deletions(-) rename pfSense-pkg-API/files/etc/inc/api/{api_calls => api_models}/APIAccessToken.inc (96%) create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNat.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarp.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarpModify.inc diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 06b0d1e7c..aee6f4b48 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -56,134 +56,244 @@ diving into creating endpoints. To get started writing your own endpoints, pleas - Delete actions must be a `POST` request to an endpoint ending in `/delete/` (e.g. `https://localhost/api/v1/firewall/rules/delete/`) -### Writing the API caller ### -At the core of the API endpoint is the API caller. This is a function that validates client request data, writes changes -to the pfSense configuration file, makes any corresponding system changes, and returns the requested data as an array. - -1. Most API endpoints are designed to allow programmatic changes to configuration that is usually made in the pfSense -webConfigurator. To get a basic understanding on what your API call will need to do, look at the PHP code of the -corresponding webConfigurator page (found within `/usr/local/www/` of your pfSense installation). You should be able to -get a good idea of what input validation is needed, and what core pfSense functions you will need to call to achieve -these changes. You can also use existing API call functions (found in `/files/etc/inc/apicalls.inc` within this repo) as -a reference! - -2. Write your function in `/files/etc/inc/apicalls.inc`. The function name should match the URL filepath without the -version. For example, for an API endpoint at URL `/api/v1/firewall/rules/delete/`, the function would be named -`api_firewall_rules_delete`. Please also place this function next to any related functions in `apicall.inc`. For example -if you write a new API call function for `api_firewall_rules_update`, place it next to any existing functions for the -API firewall calls. - -3. Ensure API callers always return the data of the action that was performed. For example, if you are writing an -`update` endpoint, ensure the API callers returns the updated data. Or if you are writing a `delete` endpoint, ensure -the API caller always returns the deleted data. - -4. Ensure any validation or API errors encountered when the API caller function is run returns a corresponding error -from the `/files/etc/inc/apiresp.inc` file. You can read more about writing API error responses below. - -Here is an example structure of an API caller function: +### Writing the API model ### +At the core of the API endpoint is the API model. This is a class that validates client request data, writes changes +to the pfSense configuration file, and makes any corresponding system changes. pfSense API is distributed with a +custom microframework to accommodate developers wanting to contribute or create their own API models and endpoints. + +#### Getting Started #### +To get started creating a new API model, you first need to create a new PHP file in `/files/etc/inc/api/api_models` and +create a new class that extends our APIBaseModel framework class: + +```php + "ok", "code" => 200, "return" => 0, "message" => "", "data" => []); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; // Returns default unauthorized response + +#### Constructing the API Model #### +In order to use the APIBaseModel framework, you must add a `__construct()` method to your new API model class and +initialize the APIBaseModel class as such. Additionally, you will specify your model attribute overrides within this +method: + +```php +methods = ["POST"]; + $this->privileges = ["page-all", "page-diagnostics-arptable"]; + $this->requires_auth = false; } } ``` -### Writing API responses ### -The API uses a centralized API response array (found in `/file/etc/inc/apiresp.inc` of this repo). Each response -corresponds with a unique code that can be used to get the response message, status, etc. This is particularly helpful -when API response messages need to be changed as it is always in one central location. +#### Overriding Base Model Properties #### +There are several class properties that you can customize to fit the needs of your API Model. If a model attribute is +not specified, the default values are assumed: -To create a new API response: +- `$this->methods` : Allows you to set what HTTP methods are allowed in array format. Due limitations of pfSense's NGINX + configuration, only GET and POST requests can be used. Defaults to `["GET", "POST"]`. + +- `$this->privileges` : Allows you to set what pfSense permissions are required for clients to access this model. This +utilizes the same permissions as the pfSense webConfigurator. Specify the privileges internal config value in array +format. Defaults to `["page-all"]` which requires client to have the 'WebCfg - All Pages' permission. -1. Pick a numeric code that is not already in use in the `$err_lib` array within the `api_error_lib()` function of -`/files/etc/inc/apiresp.inc`. Try to use a code that is within the reserved range of the API endpoint you are creating. -2. Add a new array item to the `$err_lib` array within the `api_error_lib()`. The associated ID should be the numeric -code you picked, and the value should be the descriptive message to return from the API. Some examples are: -``` -$err_lib = array( - // 0-999 reserved for system API level responses - 0 => "Success", - 1 => "Process encountered unexpected error", - 2 => "Invalid HTTP method", - 3 => "Authentication failed" -) +- `$this->requires_auth` : Specify whether authentication and authorization is required for the API model. If set to +`false` clients will not have to authenticate or have privilege to access. Defaults to `true`. + +- `$this->set_auth_mode` : Allows you to statically specify the API authentication mode. For example, if you are +writing a model that tests user's local database credentials and do not want the model to assume the API's configured +auth mode you would specify `$this->set_auth_mode = "local";` to always force local authentication. Defaults to the +API's configured auth mode in the /api/ webConfigurator page. + +- `$this->validators` : Allows you to specify function calls that validate the API payload data. More information on +writing model validators is included below. Defaults to `[]` which does not provide any validation. + + +#### Other Base Model Properties #### +There are other properties inherited from APIBaseModel that are not intended (and shouldn't be) overridden by your +custom API model: + +- `$this->client` : An APIAuth object that contains information about the client (for more see Accessing Client Data) +- `$this->initial_data` : The request data as it was when the object was created +- `$this->validated_data` : An array for validators to use to populate data that has been validated +- `$this->errors` : An array to populate any errors encountered. Should be an array of APIResponse values. + +#### Writing API Model Validators #### +By default, API models do not provide any sort of validation. You are responsible for writing the class methods to +validate the request. Validator methods are essentially class methods that check specific field(s) in our +`$this->initial_data` property and either adds them to our `$this->validated_data` property, or adds an error response +to `$this->errors`. + +For example: +```php +methods = ["POST"]; + $this->privileges = ["page-all", "page-diagnostics-arptable"]; + $this->requires_auth = false; + + # CALL YOUR VALIDATOR METHODS HERE. This must be an array of function calls! + $this->validators = [ + $this->validateTest() + ]; + } + + # Create a new validator method that validates our payloads 'test' value. + private function validateTest() { + # Check if we have a test value in our payload, if so add the value to validated_data array + if (array_key_exists("test", $this->initial_data)) { + $this->validated_data["test"] = $this->initial_data["test"]; + } + # Otherwise, append a new error response to our error list (see Writing API responses for more info) + else { + $this->errors[] = APIResponse\get(1); + } + } +} ``` -3. To get the response message, ensure your API caller function has `$err_lib` declared globally -(e.g. `global $err_lib;`), then you can pull the message corresponding with your code as such `$err_lib[]`. Each -API caller should return a response error similar to: + + +#### Writing API Model Action #### +By default, the API model will return a 'Your API request was valid but no actions were specified for this endpoint' +error after validating input. This is because we need to tell it what to do when our request is valid! We do this by +overriding the APIBaseModel's `action()` method. This method should simply be a set of tasks to perform upon a +successful API call. If you are writing an endpoint to perform a change that is usually performed via the pfSense +webConfigurator, it may be helpful to look at the PHP code for the webConfigurator page. + +As a basic example, if I wanted to add our validated input to our pfSense configuration: + +```php +class NewAPIModel extends APIBaseModel { + public function __construct() { + parent::__construct(); + $this->methods = ["POST"]; + $this->privileges = ["page-all", "page-diagnostics-arptable"]; + $this->requires_auth = false; + + # CALL YOUR VALIDATOR METHODS HERE. This must be an array of function calls! + $this->validators = [ + $this->validateTest() + ]; + } + + private function validateTest() { + if (array_key_exists("test", $this->initial_data)) { + $this->validated_data["test"] = $this->initial_data["test"]; + } + else { + $this->errors[] = APIResponse\get(1); + } + } + + # Tell our API model what to do after successfully validating the client's request + public function action(){ + $_SESSION["Username"] = $this->client->username; // Save our user to session data for logging + $change_note = " Added TEST value via API"; // Add a change note + $config["test"][] = $this->validated_data; // Write a new 'test' item to our master config + write_config(sprintf(gettext($change_note))); // Apply our configuration change + return APIResponse\get(0, $this->validated_data); // Return a success response containing our added data + } +} ``` -array( - "status" => "unauthorized", # Sets the descriptive HTTP response message (unauthorized, not found, ok, etc.) - "code" => 401, # Sets the HTTP response code. This will be the response code PHP returns to the client. - "return" => 3, # Set the unique API response code from `apiresp.inc` - "message" => $err_lib[3], # Pull the message corresponding with this unique response code from `apiresp.inc` - "data" => [] # Set the data to return to the client in an array format -);` + + +#### Writing API endpoints #### +API models are not useful if we do not have an API endpoint that calls upon the model. API endpoints are the PHP scripts +that will be placed in pfSense's web path. To create a new API endpoint, add an index.php file in +`/files/usr/local/www/api/v1/` wherever makes the most sense for your API call. You may also create new directories +if none of the existing ones describe your API call well enough. For example, if I wanted to create a new API call +that would be available at https://pfsensehost/api/v1/system/test/, I would need to create a new directory within +/files/usr/local/www/api/v1/system/ called 'test' and add my index.php in that directory. + +All that is needed for API endpoints is the following. Be sure to change the API model class name to your own: +```php +listen(); ``` -### Writing API endpoint listeners ### -Each API caller must have an API endpoint listener within the web path to listen for requests and execute the API caller -function. These can be found in `/files/usr/local/www/api/v1/`. To create a new endpoint: -1. Create a new directory in `/files/usr/local/www/api/v1/` that corresponds with the endpoint you are creating. For -example, if you are creating a new endpoint that deletes a firewall rule, you would create the directory -`/files/usr/local/www/api/v1/firewall/rules/delete/`. -2. Create an index.php file within that directory and add the following code: + +#### Accessing Client Data #### +If for any reason you need to access client data from within your API model class, you can access the `$this->client` +property. This is an APIAuth object that contains details about the client: + +- `$this->client->username` : Our client's corresponding pfSense username +- `$this->client->is_authenticated` : Whether the client successfully authenticated. Keep in mind the APIBaseModel will +handle all authentication and authorization for you, so this is likely unneeded. +- `$this->client->is_authorized` : Whether the client was authorized. Keep in mind the APIBaseModel will +handle all authentication and authorization for you, so this is likely unneeded. +- `$this->client->privs` : An array of privileges this user has +- `$this->client->auth_mode` : The authentication mode that was used to authenticate the user + +### Writing API responses ### +The API uses a centralized API response array (found in `/files/etc/inc/api/framework/APIResponse.inc` of this repo). +Each response corresponds with a unique ID that can be used to get the API response message, status, etc. This is +particularly helpful when API response messages need to be changed as it is always in one central location. To add a +new API response, you may add a new array item to the `$responses` variable within the `get()` function of +`/files/etc/inc/api/framework/APIResponse.inc`. Each response within the array should be formatted as an associative +array with the `status`, `code`, `return`, and `message` keys. + +For example, if I needed to write a new error response for API endpoint I could add: + ``` - [ + "status" => "bad request", # Use this field to describe the HTTP response (not found, bad request, ok, etc.) + "code" => 400, # Use this field to set the HTTP response code that will be returned to the client + "return" => $id, # This should always return the API response ID, in this case 620. + "message" => "Error found!" # Set a descriptive response message +] ``` -This expects the API caller function to return a associative array. This will then set the HTTP response code based on -the "code" value of your functions returned array. The returned array with then be serialized to JSON and returned to -the client. + +After this response item is added to to the `$responses` variable within the `get()` function of +`/files/etc/inc/api/framework/APIResponse.inc`, you can get the response within your API model like this: + +`$this->errors[] = APIResponse\get(620);` + +Optionally, you can add data to the response as a parameter of the `get()` function like this: + +`$this->errors[] = APIResponse\get(620, $some_data);` + ### Writing tool functions ### Often times you will need to create functions to condense redundant tasks. You can place any necessary tool functions in -`/files/etc/inc/api.inc`. +`/files/etc/inc/api/framework/APITools.inc`. You may then access the tool function from your API model like this: + +`$some_variable = APITools\your_custom_tool_function();` -### Adding endpoint to the package -After you have written your API endpoint and have tested it's functionality, you must specify your endpoint files in -the package makefile. Otherwise, it will not be included in the package in the next release. +### Adding Models and Endpoints to the Package +After you have written your API models and endpoint and have tested it's functionality, you must specify your endpoint +files in the package makefile. Otherwise, it will not be included in the package in the next release. 1. Add the following lines to the `Makefile` located in this repo. **Be sure to change the file paths to match the files you have created**: ``` -${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/status/carp/modify -${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/status/carp/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/status/carp/modify +${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/test +${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/test/index.php \ + ${STAGEDIR}${PREFIX}/www/api/v1/system/test ``` 2. Add the following lines to the `pkg-plist` file located in this repo. Be sure to change the file paths to match the files you have created: - For each directory created, add: `@dir /usr/local/www/api/v1/status/carp/modify` - - For each index.php file created, add `/usr/local/www/api/v1/status/carp/modify/index.php` + - For each index.php endpoint created, add `/usr/local/www/api/v1/status/carp/modify/index.php` + - For each API model file created, add `/etc/inc/api/api_models/YourAPIModel.inc` + Questions --------- diff --git a/pfSense-pkg-API/Makefile b/pfSense-pkg-API/Makefile index a1838cd41..7e3726cdf 100644 --- a/pfSense-pkg-API/Makefile +++ b/pfSense-pkg-API/Makefile @@ -18,436 +18,8 @@ SUB_LIST= PORTNAME=${PORTNAME} do-extract: ${MKDIR} ${WRKSRC} -do-install: +do-intall: + @echo "YES" - # INSTALL OUR API INCLUDE FILE - ${MKDIR} ${STAGEDIR}/etc/inc - ${INSTALL_DATA} ${FILESDIR}/etc/inc/api.inc \ - ${STAGEDIR}/etc/inc - ${INSTALL_DATA} ${FILESDIR}/etc/inc/apicalls.inc \ - ${STAGEDIR}/etc/inc - ${INSTALL_DATA} ${FILESDIR}/etc/inc/apiresp.inc \ - ${STAGEDIR}/etc/inc - # INSTALL STATIC PHP-JWT FILES (static files are required as pfSense does not allow composer installations) - ${MKDIR} ${STAGEDIR}/etc/inc/php-jwt - ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/README.md \ - ${STAGEDIR}/etc/inc/php-jwt - ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/LICENSE \ - ${STAGEDIR}/etc/inc/php-jwt - ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/composer.json \ - ${STAGEDIR}/etc/inc/php-jwt - ${MKDIR} ${STAGEDIR}/etc/inc/php-jwt/src - ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/JWT.php \ - ${STAGEDIR}/etc/inc/php-jwt/src - ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/JWK.php \ - ${STAGEDIR}/etc/inc/php-jwt/src - ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/SignatureInvalidException.php \ - ${STAGEDIR}/etc/inc/php-jwt/src - ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/ExpiredException.php \ - ${STAGEDIR}/etc/inc/php-jwt/src - ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/BeforeValidException.php \ - ${STAGEDIR}/etc/inc/php-jwt/src - - # INSTALL OUR PFSENSE PKG - ${MKDIR} ${STAGEDIR}${PREFIX}/pkg - ${INSTALL_DATA} -m 0644 ${FILESDIR}${PREFIX}/pkg/api.xml \ - ${STAGEDIR}${PREFIX}/pkg - - # INSTALL OUR API ENDPOINTS - # API Base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/index.php \ - ${STAGEDIR}${PREFIX}/www/api - # API version - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1 - # ACCESS TOKEN API ENDPOINTS - # Access Token base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/access_token - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/access_token/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/access_token - # USERS API ENDPOINTS------------------------------------------ - # Users base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users - # Users add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/add - # Users priv add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/add/privs - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/add/privs/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/add/privs - # Users group add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/add/groups - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/add/groups/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/add/groups - # Users delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/delete - # Users priv delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/delete/privs - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/delete/privs/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/delete/privs - # Users group delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/delete/groups - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/delete/groups/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/delete/groups - # Users modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/modify - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/modify - # Authservers base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/authservers/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers - # Authservers delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/authservers/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/delete - # Authservers ldap - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/ldap - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/authservers/ldap/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/ldap - # Authservers ldap add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/ldap/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/authservers/ldap/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/ldap/add - # Authservers ldap delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/ldap/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/authservers/ldap/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/ldap/delete - # Authservers radius - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/radius - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/authservers/radius/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/radius - # Authservers radius delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/radius/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/users/authservers/radius/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/users/authservers/radius/delete - # SYSTEM API ENDPOINTS---------------------------------------- - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/ - # Version base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/version - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/version/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/version - # API base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/api - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/api/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/api - # API errors base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/api/errors - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/api/errors/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/api/errors - # ARP base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/arp - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/arp/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/arp - # ARP delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/arp/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/arp/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/arp/delete - # Config base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/config - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/config/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/config - # Hostname base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/hostname - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/hostname/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/hostname - # Hostname modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/hostname/modify - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/hostname/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/hostname/modify - # DNS base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/dns - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/dns/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/dns - # DNS modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/dns/modify - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/dns/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/dns/modify - # DNS delete servers - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/dns/delete - # DNS delete servers - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/dns/delete/servers - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/dns/delete/servers/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/dns/delete/servers - # Certificates base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/certificates - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/certificates/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/certificates - # Certificates add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/certificates/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/certificates/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/certificates/add - # Certificates delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/certificates/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/certificates/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/certificates/delete - # STATUS API ENDPOINTS---------------------------------------- - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/status/ - # CARP base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/status/carp - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/status/carp/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/status/carp - # CARP base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/status/carp/modify - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/status/carp/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/status/carp/modify - - # INTERFACES API ENDPOINTS---------------------------------------- - # Interfaces base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/interfaces - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/interfaces/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/interfaces - # Interfaces add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/interfaces/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/add - # Interfaces delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/interfaces/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/delete - # Vlans base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/vlans - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/interfaces/vlans/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/vlans - # Vlans add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/vlans/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/interfaces/vlans/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/vlans/add - # Vlans delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/vlans/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/interfaces/vlans/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/vlans/delete - # Vlans modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/vlans/modify - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/interfaces/vlans/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/interfaces/vlans/modify - - # ROUTING API endpoints---------------------------------------- - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/routing - # Gateways base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/routing/gateways - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/routing/gateways/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/routing/gateways - - # FIREWALL API ENPOINTS - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall - # STATES base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/states - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/states/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/states - # STATES size - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/states/size - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/states/size/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/states/size - # STATES size modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/states/size/modify - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/states/size/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/states/size/modify - # NAT base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/nat - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/nat/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/nat - # NAT port forwards base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards - # NAT port forwards add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards/add - # NAT port forwards add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/nat/portforwards/delete - # Virtual IP base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/virtualips - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/virtualips/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/virtualips - # Virtual IP add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/virtualips/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/virtualips/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/virtualips/add - # Virtual IP delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/virtualips/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/virtualips/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/virtualips/delete - # Alias base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/aliases/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases - # Alias add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/aliases/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/add - # Alias add address - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/add/address - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/aliases/add/address/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/add/address - # Alias delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/aliases/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/delete - # Alias delete address - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/delete/address - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/aliases/delete/address/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/delete/address - # Alias modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/modify - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/aliases/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/aliases/modify - # Rules base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/rules - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/rules/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/rules - # Rules add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/rules/add - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/rules/add/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/rules/add - # Rules delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/firewall/rules/delete - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/firewall/rules/delete/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/firewall/rules/delete - - # SERVICE API ENDPOINTS---------------------------------------- - # Services base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services - # Base start - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/start - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/start/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/start - # Base stop - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/stop - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/stop/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/stop - # Base restart - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/restart - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/restart/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/restart - # DHCPD base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/dhcpd - # DHCPD start - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/dhcpd/start - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/dhcpd/start/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/dhcpd/start - # DHCPD stop - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/dhcpd/stop - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/dhcpd/stop/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/dhcpd/stop - # DHCPD restart - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/dhcpd/restart - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/dhcpd/restart/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/dhcpd/restart - # DPINGER base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/dpinger - # DPINGER start - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/dpinger/start - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/dpinger/start/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/dpinger/start - # DPINGER stop - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/dpinger/stop - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/dpinger/stop/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/dpinger/stop - # DPINGER restart - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/dpinger/restart - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/dpinger/restart/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/dpinger/restart - # NTPD base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/ntpd - # NTPD start - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/ntpd/start - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/ntpd/start/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/ntpd/start - # NTPD stop - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/ntpd/stop - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/ntpd/stop/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/ntpd/stop - # NTPD restart - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/ntpd/restart - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/ntpd/restart/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/ntpd/restart - # SSHD base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/sshd/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd - # SSHD modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd/modify - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/sshd/modify/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd/modify - # SSHD start - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd/start - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/sshd/start/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd/start - # SSHD stop - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd/stop - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/sshd/stop/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd/stop - # SSHD restart - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd/restart - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/sshd/restart/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/sshd/restart - # SYSLOGD base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/syslogd - # SYSLOGD start - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/syslogd/start - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/syslogd/start/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/syslogd/start - # SYSLOGD stop - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/syslogd/stop - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/syslogd/stop/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/syslogd/stop - # SYSLOGD restart - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/syslogd/restart - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/syslogd/restart/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/syslogd/restart - # Unbound base - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/unbound/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound - # Unbound start - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/start - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/unbound/start/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/start - # Unbound stop - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/stop - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/unbound/stop/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/stop - # Unbound restart - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/restart - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/unbound/restart/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/restart - # Unbound add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/add - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/add/hosts - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/unbound/add/hosts/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/add/hosts - # Unbound delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/delete - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/delete/hosts - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/unbound/delete/hosts/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/delete/hosts - # Unbound modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/modify - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/modify/hosts - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/unbound/modify/hosts/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/modify/hosts - # Unbound apply - ${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/apply - ${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/services/unbound/apply/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/services/unbound/apply - - # INSTALL OUR PKG INFO - ${MKDIR} ${STAGEDIR}${DATADIR} - ${INSTALL_DATA} ${FILESDIR}${DATADIR}/info.xml \ - ${STAGEDIR}${DATADIR} - @${REINPLACE_CMD} -i '' -e "s|%%PKGVERSION%%|${PKGVERSION}|" \ - ${STAGEDIR}${DATADIR}/info.xml - .include \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api.inc b/pfSense-pkg-API/files/etc/inc/api.inc index ae8a71c95..46e18ba17 100644 --- a/pfSense-pkg-API/files/etc/inc/api.inc +++ b/pfSense-pkg-API/files/etc/inc/api.inc @@ -1304,7 +1304,7 @@ function get_carp_if_status() { // Enables CARP interfaces function enable_carp($enable) { // Local variables - global $err_lib, $config; + global $config; $vip_arr = $config['virtualip']['vip']; $no_action = (is_carp_enabled() === $enable) ? true : false; // Check if a change is even requried // Disable if $enable is false, enable if $enable is true diff --git a/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIAccessToken.inc similarity index 96% rename from pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc rename to pfSense-pkg-API/files/etc/inc/api/api_models/APIAccessToken.inc index abfa66458..0f4966ee1 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_calls/APIAccessToken.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIAccessToken.inc @@ -23,7 +23,7 @@ class APIAccessToken extends APIBaseModel { } } - # Override action subclass to create a JWT and return it to the user + # Override action subclass to create a JWT and return it to the user after successful validation public function action() { $jwt = APITools\create_jwt($this->client->username); return APIResponse\get(0, ["token" => $jwt]); diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNat.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNat.inc new file mode 100644 index 000000000..3c0d8b3ae --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNat.inc @@ -0,0 +1,22 @@ +methods = ["GET"]; + $this->validators = []; + } + + public function action() { + global $config; + // Check that we have a NAT configuration + if (!empty($config["nat"])) { + $this->validated_data = $config["nat"]; + } + return APIResponse\get(0, $this->validated_data); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarp.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarp.inc new file mode 100644 index 000000000..67501bbfd --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarp.inc @@ -0,0 +1,23 @@ +privileges = ["page-all", "page-status-carp"]; + $this->methods = ["GET"]; + $this->validators = []; + } + + public function action() { + global $config; + $carp_status = []; + $carp_status["enable"] = APITools\is_carp_enabled(); + $carp_status["maintenance_mode"] = isset($config["virtualip_carp_maintenancemode"]); + $carp_status["interfaces"] = APITools\get_carp_if_status(); + return APIResponse\get(0, $carp_status); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarpModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarpModify.inc new file mode 100644 index 000000000..77df3679c --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarpModify.inc @@ -0,0 +1,45 @@ +privileges = ["page-all", "page-status-carp"]; + $this->methods = ["POST"]; + $this->validators = [$this->validate_payload()]; + } + + private function validate_payload() { + // Check if user specified enable value + if (isset($this->initial_data['enable'])) { + // Check if value is true or false + if (boolval($this->initial_data['enable'])) { + $enable = true; + } else { + $enable = false; + } + $this->validated_data["enable"] = $enable; + } + // Check if user specified maintenance mode value + if (isset($this->initial_data['maintenance_mode'])) { + // Check if value is true or false + if (boolval($this->initial_data['maintenance_mode'])) { + $mm_enable = true; + } else { + $mm_enable = false; + } + $this->validated_data["maintenance_mode"] = $mm_enable; + } + } + + public function action() { + // Add our CARP settings + $_SESSION["Username"] = $this->client->username; + interfaces_carp_set_maintenancemode($this->validated_data["maintenance_mode"]); + APITools\enable_carp($this->validated_data["enable"]); + return APIResponse\get(0, $this->validated_data); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc index 5fde932f9..f7ff708c2 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc @@ -61,7 +61,7 @@ class APIAuth { # Attempts to authenticate using API token authentication private function authenticate_token() { - if (APITools\authenticate_token($this->request["client-id"], $this->request["client-id"]) === true) { + if (APITools\authenticate_token($this->request["client-id"], $this->request["client-token"]) === true) { $this->username = pack("H*", $this->request["client-id"]); // Ensure user is not disabled if (APITools\is_user_disabled($this->request["client-id"]) === false) { diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc index 01add862a..9bda5c703 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc @@ -31,7 +31,7 @@ class APIBaseModel { # This function is intended to be overridden by an API model extended class # Any configuration writes, system configurations, etc should be added when overriding this base class # If this class is not overridden a 500 unexpected error is returned - return APIResponse\get(1); + return APIResponse\get(10); } public function validate() { diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc index 97ad06594..a99f47dbd 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc @@ -65,6 +65,12 @@ function get($id, $data=[]) { "return" => $id, "message" => "Authentication mode must be set to JWT to enable access token authentication", ], + 10 => [ + "status" => "server error", + "code" => "500", + "return" => $id, + "message" => "Your API request was valid but no actions were specified for this endpoint", + ] ]; $response = $responses[$id]; diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index 84a448afc..84fedf21c 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -14,9 +14,6 @@ require_once("shaper.inc"); require_once("auth.inc"); require_once("functions.inc"); use Firebase\JWT\JWT; -use Firebase\JWT\ExpiredException; -use Firebase\JWT\SignatureInvalidException; -use Firebase\JWT\BeforeValidException; # Gathers our URL form encoded data or JSON body data from our request and places them in a single array function get_request_data() { @@ -121,7 +118,7 @@ function decode_jwt($token) { $key = get_api_config()[1]["server_key"]; // Save our current server key try { $decoded = (array) JWT::decode($token, $key, array('HS256')); - } catch (Exception $e) { + } catch (\Exception $e) { $decoded = false; } return $decoded; @@ -180,4 +177,79 @@ function generate_token($username) { $change_note = " Generated API key"; // Add a change note write_config(sprintf(gettext($change_note))); // Apply our configuration change return $key_new; +} + +// Check if CARP is enabled for disabled +function is_carp_enabled() { + // Check current CARP status + $status = get_single_sysctl('net.inet.carp.allow'); + $enabled = boolval(intval($status) > 0); + return $enabled; +} + +// Check each CARP interface's status +function get_carp_if_status() { + // Local variables + global $err_lib, $config; + $carp_if_stats = []; + $carp_enabled = is_carp_enabled(); + foreach ($config['virtualip']['vip'] as $carp) { + if ($carp['mode'] == "carp") { + $carp_if_ent = []; + $carp_if_ent["interface"] = $carp["interface"]; + $carp_if_ent["vhid"] = $carp['vhid']; + $carp_if_ent["subnet"] = $carp['subnet']; + $carp_if_ent["subnet_bits"] = $carp['subnet_bits']; + $status = get_carp_interface_status("_vip{$carp['uniqid']}"); + if ($carp_enabled == false) { + $carp_if_ent["status"] = "disabled"; + } else { + if ($status == "MASTER") { + $carp_if_ent["status"] = "master"; + } else if ($status == "BACKUP") { + $carp_if_ent["status"] = "backup"; + } else if ($status == "INIT") { + $carp_if_ent["status"] = "init"; + } + } + // Add config to our array + $carp_if_stats[] = $carp_if_ent; + } + } + // Return our status + return $carp_if_stats; +} + +// Enables CARP interfaces +function enable_carp($enable) { + // Local variables + global $config; + $vip_arr = $config['virtualip']['vip']; + $no_action = (is_carp_enabled() === $enable) ? true : false; // Check if a change is even requried + // Disable if $enable is false, enable if $enable is true + if (!$no_action and $enable === false) { + set_single_sysctl('net.inet.carp.allow', '0'); + foreach ($vip_arr as $vip) { + if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias") + continue; + if ($vip['mode'] == "ipalias" && substr($vip['interface'], 0, 4) != "_vip") + continue; + interface_vip_bring_down($vip); + } + } elseif (!$no_action and $enable === true) { + foreach ($vip_arr as $vip) { + switch ($vip['mode']) { + case "carp": + interface_carp_configure($vip); + break; + case 'ipalias': + if (substr($vip['interface'], 0, 4) == "_vip") { + interface_ipalias_configure($vip); + } + break; + } + } + interfaces_sync_setup(); + set_single_sysctl('net.inet.carp.allow', '1'); + } } \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/apicalls.inc b/pfSense-pkg-API/files/etc/inc/apicalls.inc index 1b8ab4abd..0ec3026c4 100644 --- a/pfSense-pkg-API/files/etc/inc/apicalls.inc +++ b/pfSense-pkg-API/files/etc/inc/apicalls.inc @@ -128,7 +128,7 @@ function api_firewall_nat() { # VARIABLES global $err_lib, $config, $api_resp, $client_params; $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-virtualipaddresses"); // Array of privs allowed + $req_privs = array("page-all"); // Array of privs allowed $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method $nat_array = array(); // Init our array # RUN TIME diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php index 84237de5c..57666837a 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/access_token/index.php @@ -1,7 +1,7 @@ listen(); diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/index.php index cf32472e1..7dcc29f82 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/index.php @@ -1,10 +1,5 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/status/carp/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/status/carp/index.php index d8a11321d..ac533906d 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/status/carp/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/status/carp/index.php @@ -1,10 +1,5 @@ listen(); -# RUN API CALL -$resp = api_status_carp(); -http_response_code($resp["code"]); -echo json_encode($resp) . PHP_EOL; -exit(); diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/status/carp/modify/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/status/carp/modify/index.php index c6cd46493..3ce64f209 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/status/carp/modify/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/status/carp/modify/index.php @@ -1,10 +1,5 @@ listen();; -# RUN API CALL -$resp = api_status_carp_modify(); -http_response_code($resp["code"]); -echo json_encode($resp) . PHP_EOL; -exit(); From c138176c0e7572f0c397b18e2f911dc2506363aa Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Sat, 22 Aug 2020 11:09:35 -0600 Subject: [PATCH 08/16] Updated contributor doc, migrated API endpoints from function based to class based for APIFirewallVirtualIPs and APIFirewallNAT, started migrating APIFirewallRules --- CONTRIBUTING.md | 29 +-- pfSense-pkg-API/files/etc/inc/api.inc | 2 +- .../etc/inc/api/api_models/APIAccessToken.inc | 7 +- .../api_models/APIFirewallNatPortForwards.inc | 21 ++ .../APIFirewallNatPortForwardsAdd.inc | 173 +++++++++++++++ .../APIFirewallNatPortForwardsDelete.inc | 38 ++++ .../inc/api/api_models/APIFirewallRules.inc | 24 +++ .../api/api_models/APIFirewallVirtualIPs.inc | 24 +++ .../api_models/APIFirewallVirtualIPsAdd.inc | 143 ++++++++++++ .../APIFirewallVirtualIPsDelete.inc | 37 ++++ .../api/api_models/APIStatusCarpModify.inc | 3 +- .../etc/inc/api/framework/APIBaseModel.inc | 11 +- .../etc/inc/api/framework/APIResponse.inc | 189 +++++++++++++++- .../files/etc/inc/api/framework/APITools.inc | 204 ++++++++++++++++++ pfSense-pkg-API/files/etc/inc/apicalls.inc | 2 +- .../firewall/nat/portforwards/add/index.php | 9 +- .../nat/portforwards/delete/index.php | 10 +- .../v1/firewall/nat/portforwards/index.php | 9 +- .../local/www/api/v1/firewall/rules/index.php | 10 +- .../api/v1/firewall/virtualips/add/index.php | 10 +- .../v1/firewall/virtualips/delete/index.php | 10 +- .../www/api/v1/firewall/virtualips/index.php | 10 +- 22 files changed, 889 insertions(+), 86 deletions(-) create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwards.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRules.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPs.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index aee6f4b48..f2be02dfa 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -105,7 +105,8 @@ not specified, the default values are assumed: - `$this->privileges` : Allows you to set what pfSense permissions are required for clients to access this model. This utilizes the same permissions as the pfSense webConfigurator. Specify the privileges internal config value in array -format. Defaults to `["page-all"]` which requires client to have the 'WebCfg - All Pages' permission. +format. Defaults to `["page-all"]` which requires client to have the 'WebCfg - All Pages' permission. A list of pfSense +privileges can be found here: https://github.com/pfsense/pfsense/blob/master/src/etc/inc/priv.defs.inc - `$this->requires_auth` : Specify whether authentication and authorization is required for the API model. If set to `false` clients will not have to authenticate or have privilege to access. Defaults to `true`. @@ -115,9 +116,6 @@ writing a model that tests user's local database credentials and do not want the auth mode you would specify `$this->set_auth_mode = "local";` to always force local authentication. Defaults to the API's configured auth mode in the /api/ webConfigurator page. -- `$this->validators` : Allows you to specify function calls that validate the API payload data. More information on -writing model validators is included below. Defaults to `[]` which does not provide any validation. - #### Other Base Model Properties #### There are other properties inherited from APIBaseModel that are not intended (and shouldn't be) overridden by your @@ -128,11 +126,11 @@ custom API model: - `$this->validated_data` : An array for validators to use to populate data that has been validated - `$this->errors` : An array to populate any errors encountered. Should be an array of APIResponse values. -#### Writing API Model Validators #### -By default, API models do not provide any sort of validation. You are responsible for writing the class methods to -validate the request. Validator methods are essentially class methods that check specific field(s) in our -`$this->initial_data` property and either adds them to our `$this->validated_data` property, or adds an error response -to `$this->errors`. +#### Overriding API Model Validation #### +By default, API models do not provide any sort of validation. You are responsible for overriding the class method to +validate the request. To validate requests, you must override the `validate_payload()` method to check specific field(s) +in the `$this->initial_data` property and either add them to our `$this->validated_data` property if they are valid, or +appends an error response to the `$this->errors` property. For example: ```php @@ -145,15 +143,10 @@ class NewAPIModel extends APIBaseModel { $this->methods = ["POST"]; $this->privileges = ["page-all", "page-diagnostics-arptable"]; $this->requires_auth = false; - - # CALL YOUR VALIDATOR METHODS HERE. This must be an array of function calls! - $this->validators = [ - $this->validateTest() - ]; } - # Create a new validator method that validates our payloads 'test' value. - private function validateTest() { + # Overrides our APIBaseModel validate_payload method to validate data within our initial_data property + public function validate_payload() { # Check if we have a test value in our payload, if so add the value to validated_data array if (array_key_exists("test", $this->initial_data)) { $this->validated_data["test"] = $this->initial_data["test"]; @@ -186,11 +179,11 @@ class NewAPIModel extends APIBaseModel { # CALL YOUR VALIDATOR METHODS HERE. This must be an array of function calls! $this->validators = [ - $this->validateTest() + $this->validate_payload() ]; } - private function validateTest() { + public function validate_payload() { if (array_key_exists("test", $this->initial_data)) { $this->validated_data["test"] = $this->initial_data["test"]; } diff --git a/pfSense-pkg-API/files/etc/inc/api.inc b/pfSense-pkg-API/files/etc/inc/api.inc index 46e18ba17..fe8056bbb 100644 --- a/pfSense-pkg-API/files/etc/inc/api.inc +++ b/pfSense-pkg-API/files/etc/inc/api.inc @@ -789,7 +789,7 @@ function sort_firewall_rules($mode=null, $data=null) { // Sorts nat rules by specified criteria and reloads the filter function sort_nat_rules($mode=null, $data=null) { // Variables - global $err_lib, $config; + global $config; $sort_arr = []; $master_arr = []; foreach ($config["nat"]["rule"] as $idx => $fre) { diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIAccessToken.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIAccessToken.inc index 0f4966ee1..c5478a112 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIAccessToken.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIAccessToken.inc @@ -8,18 +8,15 @@ class APIAccessToken extends APIBaseModel { parent::__construct(); $this->set_auth_mode = "local"; $this->methods = ["POST"]; - $this->validators = [ - $this->validateAuthMode() - ]; } # Validate our API configurations auth mode (must be JWT) - private function validateAuthMode() { + public function validate_payload() { $api_config = APITools\get_api_config()[1]; # Add error if our auth mode is invalid if ($api_config["authmode"] !== "jwt") { - return APIResponse\get(9); + $this->errors[] = APIResponse\get(9); } } diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwards.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwards.inc new file mode 100644 index 000000000..6a0e91c07 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwards.inc @@ -0,0 +1,21 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-firewall-nat-portforward"]; + } + + public function action() { + global $config; + // Check that we have a virtual IP configuration + if (!empty($config["nat"]["rule"])) { + $this->validated_data = $config["nat"]["rule"]; + } + return APIResponse\get(0, $this->validated_data); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc new file mode 100644 index 000000000..210264d77 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc @@ -0,0 +1,173 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-nat-portforward-edit"]; + } + + public function action() { + global $config; + $_SESSION["Username"] = $this->client->username; + $change_note = " Added NAT rule via API"; // Add a change note + $config["nat"]["rule"][] = $this->validated_data; // Write to our master config + $next_rule_id = count($config["nat"]["rule"]); // Save our next rule ID + APITools\sort_nat_rules($this->initial_data["top"], $next_rule_id); // Sort our nat rules + write_config(sprintf(gettext($change_note))); // Apply our configuration change + filter_configure(); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $this->validated_data); + } + + // TODO: break this down into smaller field specific validators + public function validate_payload() { + global $config; + $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client + $allowed_nat_ref = ["enable", "disable", "purenat"]; // Save our allow NAT reflection types + $allowed_prot = ["tcp", "udp", "tcp/udp", "icmp", "esp", "ah", "gre", "ipv6", "igmp", "pim", "ospf"]; + if (isset($this->initial_data['interface'])) { + $interface = $this->initial_data['interface']; + $interface = APITools\get_pfsense_if_id($interface); + } else { + $this->errors[] = APIResponse\get(4000); + } + if (isset($this->initial_data['protocol'])) { + $protocol = $this->initial_data['protocol']; + } else { + $this->errors[] = APIResponse\get(4001); + + } + if (isset($this->initial_data['target'])) { + $localip = $this->initial_data['target']; + } else { + $this->errors[] = APIResponse\get(4002); + } + if (isset($this->initial_data['local-port'])) { + $localport = $this->initial_data['local-port']; + } else { + $this->errors[] = APIResponse\get(4003); + } + if (isset($this->initial_data['src'])) { + $src = $this->initial_data['src']; + } else { + $this->errors[] = APIResponse\get(4004); + + } + if (isset($this->initial_data['srcport'])) { + $srcport = $this->initial_data['srcport']; + } + if (isset($this->initial_data['dst'])) { + $dst = $this->initial_data['dst']; + } else { + $this->errors[] = APIResponse\get(4005); + + } + if (isset($this->initial_data['dstport'])) { + $dstport = $this->initial_data['dstport']; + } + if (isset($this->initial_data['disabled'])) { + if ($this->initial_data['disabled']) { + $disabled = true; + } + } + if (isset($this->initial_data['nordr'])) { + if ($this->initial_data['nordr']) { + $nordr = true; + } + } + if (isset($this->initial_data['nosync'])) { + if ($this->initial_data['nosync'] === true) { + $nosync = true; + } + } + if (isset($this->initial_data['top'])) { + if ($this->initial_data['top']) { + $this->initial_data = "top"; + } + } + if (isset($this->initial_data['descr'])) { + $descr = $this->initial_data['descr']; + } + if (isset($this->initial_data['natreflection'])) { + $natreflection = $this->initial_data['natreflection']; + } + + // INPUT VALIDATION/FORMATTING + // Check that our required array/interface values are valid + if (!is_string($interface)) { + $this->errors[] = APIResponse\get(4006); + } elseif (!in_array($protocol, $allowed_prot)) { + $this->errors[] = APIResponse\get(4007); + } elseif (isset($natreflection) and !in_array($natreflection, $allowed_nat_ref)) { + $this->errors[] = APIResponse\get(4008); + } elseif (!is_ipaddrv4($localip) and !alias_in_use($localip)) { + $this->errors[] = APIResponse\get(4009); + } elseif (!is_port_or_range($localport)) { + $this->errors[] = APIResponse\get(4010); + } + $this->validated_data = array(); + // Check if rule is disabled + if ($disabled) { + $this->validated_data["disabled"] = ""; + } + // Check if pfsync is disabled + if ($nosync) { + $this->validated_data["nosync"] = ""; + } + // Check if RDR is disabled is disabled + if ($nordr) { + $this->validated_data["nordr"] = ""; + } + // Check if user specified NAT reflection + if ($natreflection) { + $this->validated_data["natreflection"] = $natreflection; + } + $this->validated_data["interface"] = $interface; + $this->validated_data["protocol"] = $protocol; + $this->validated_data["source"] = array(); + $this->validated_data["destination"] = array(); + $this->validated_data["target"] = $localip; + $this->validated_data["local-port"] = $localport; + $this->validated_data["descr"] = $descr; + $this->validated_data["associated-rule-id"] = "pass"; + $this->validated_data["created"] = array("time" => time(), "username" => $user_created_msg); + $this->validated_data["updated"] = $this->validated_data["created"]; + // Check if our source and destination values are valid + foreach (array("source" => $src, "destination" => $dst) as $dir => $val) { + $dir_check = APITools\is_valid_rule_addr($val, $dir); + if (!$dir_check["valid"]) { + if ($dir === "source") { + $this->errors[] = APIResponse\get(4011); + } else { + $this->errors[] = APIResponse\get(4012); + + } + } else { + $this->validated_data = array_merge($this->validated_data, $dir_check["data"]); + } + } + // Check if protocol calls for additional specifications + if (in_array($protocol, array("tcp", "udp", "tcp/udp"))) { + $port_req = true; + } + // Check our src and dst port values if ports are required + if ($port_req) { + foreach (array("source" => $srcport, "destination" => $dstport) as $dir => $val) { + $val = str_replace("-", ":", $val); + if (!is_port_or_range($val) and $val !== "any") { + if ($dir === "source") { + $this->errors[] = APIResponse\get(4013); + + } else { + $this->errors[] = APIResponse\get(4014); + } + } elseif ($val !== "any") { + $this->validated_data[$dir]["port"] = str_replace(":", "-", $val); + } + } + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc new file mode 100644 index 000000000..9c6e9f576 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc @@ -0,0 +1,38 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-nat-portforward-edit"]; + } + + public function action() { + global $config; + $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging + $change_note = " Deleted NAT rule via API"; // Add a change note + $del_rule = $config["nat"]["rule"][$this->validated_data["id"]]; // Save the rule we are deleting + unset($config["nat"]["rule"][$this->validated_data["id"]]); // Remove rule from our config + APITools\sort_nat_rules(); // Sort our NAT rules + write_config(sprintf(gettext($change_note))); // Apply our configuration change + filter_configure(); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $del_rule); + } + + public function validate_payload() { + global $config; + if (isset($this->initial_data['id'])) { + // Check that our rule ID exists + if (array_key_exists($this->initial_data['id'], $config["nat"]["rule"])) { + $this->validated_data["id"] = $this->initial_data['id']; + } else { + $this->errors[] = APIResponse\get(4016); + } + } else { + $this->errors[] = APIResponse\get(4015); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRules.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRules.inc new file mode 100644 index 000000000..83925285c --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRules.inc @@ -0,0 +1,24 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-firewall-rules"]; + } + + public function action() { + global $config; + // Check that we have a configuration + if (!empty($config["filter"]["rule"])) { + $rule_array = $config["filter"]["rule"]; + } else { + $rule_array = []; + } + return APIResponse\get(0, $rule_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPs.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPs.inc new file mode 100644 index 000000000..76f45f804 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPs.inc @@ -0,0 +1,24 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-firewall-virtualipaddresses"]; + } + + public function action() { + global $config; + // Check that we have a virtual IP configuration + if (!empty($config["virtualip"]["vip"])) { + $vip_array = $config["virtualip"]["vip"]; + } else { + $vip_array = []; + } + return APIResponse\get(0, $vip_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc new file mode 100644 index 000000000..f5407fca1 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc @@ -0,0 +1,143 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-virtualipaddress-edit"]; + } + + public function action() { + global $config; + $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging + $change_note = " Added virtual IP via API"; // Add a change note + $config["virtualip"]["vip"][] = $this->validated_data; // Write to our master config + write_config(sprintf(gettext($change_note))); // Apply our configuration change + APITools\apply_virtual_ip($this->validated_data); // Apply our backend changes with our new configuration + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + global $config; + $allowed_vip_modes = array("ipalias", "carp", "proxyarp", "other"); // Save our allowed vip modes in arra + if (isset($this->initial_data['mode'])) { + $mode = strtolower($this->initial_data['mode']); + } else { + $this->errors[] = APIResponse\get(4019); + } + if (isset($this->initial_data['interface'])) { + $interface = $this->initial_data['interface']; + $interface = APITools\get_pfsense_if_id($interface); + } else { + $this->errors[] = APIResponse\get(4020); + } + if (isset($this->initial_data['subnet'])) { + $subnet = $this->initial_data['subnet']; + // If a single IPv4 or IPv6, append the subnet mask for one address + if (is_ipaddrv4($subnet)) { + $subnet = $subnet."/32"; + } elseif (is_ipaddrv6($subnet)) { + $subnet = $subnet."/128"; + } + } else { + $this->errors[] = APIResponse\get(4021); + } + if (isset($this->initial_data['descr'])) { + $descr = $this->initial_data['descr']; + } else { + $descr = ""; + } + if (isset($this->initial_data['noexpand'])) { + $noexpand = true; + } + if (!in_array($mode, $allowed_vip_modes)) { + $this->errors[] = APIResponse\get(4023); + } + // CARP required attributes + if ($mode === "carp" and isset($this->initial_data['vhid'])) { + $vhid = $this->initial_data['vhid']; + } + if ($mode === "carp" and isset($this->initial_data['advskew'])) { + $advskew = intval($this->initial_data['advskew']); + } else { + $advskew = 0; // Default skew to 0 + } + if ($mode === "carp" and isset($this->initial_data['advbase'])) { + $advbase = intval($this->initial_data['advbase']); + } else { + $advbase = 1; // Default base to 0 + } + if ($mode === "carp" and isset($this->initial_data['password'])) { + $password = $this->initial_data['password']; + } else { + $this->errors[] = APIResponse\get(4022); + } + // INPUT VALIDATION/FORMATTING// Check that our required array/interface values are valid + if (!is_string($interface)) { + $this->errors[] = APIResponse\get(4024); + + } elseif (!is_subnet($subnet)) { + $this->errors[] = APIResponse\get(4025); + } + // Split our subnet into an address and subnet mask + $subnet_split = explode("/", $subnet); + $subnet = $subnet_split[0]; + $subnet_bits = $subnet_split[1]; + // Check that our subnet is not used elsewhere + if (APITools\is_ip_in_use($subnet)) { + $this->errors[] = APIResponse\get(4026); + } + // Check if VHID was specified + if (isset($vhid)) { + if (vhid_exists($vhid)) { + $this->errors[] = APIResponse\get(4027); + + } elseif (1 > $vhid or $vhid > 255) { + $this->errors[] = APIResponse\get(4028); + + } + } elseif ($mode === "carp" and !isset($vhid)) { + $vhid = APITools\next_vhid(); // Pull our next available VHID + } + // Check if advertisement base was specified + if (isset($advbase)) { + if (1 > $advbase or $advbase > 254) { + $this->errors[] = APIResponse\get(4029); + + } + } + // Check if advertisement skew was specified + if (isset($advskew)) { + if (0 > $advskew or $advskew > 254) { + $this->errors[] = APIResponse\get(4030); + + } + } + // Initialize our virtual IP configuration array + if (!is_array($config["virtualip"]["vip"])) { + $config["virtualip"] = array("vip" => []); + } + // Populate our new virtual IP entry + $this->validated_data["mode"] = $mode; + $this->validated_data["interface"] = $interface; + $this->validated_data["type"] = "network"; + $this->validated_data["subnet"] = $subnet; + $this->validated_data["subnet_bits"] = $subnet_bits; + $this->validated_data["descr"] = $descr; + // Values specific to CARP + if ($mode === "carp") { + $this->validated_data["vhid"] = $vhid; + $this->validated_data["advbase"] = $advbase; + $this->validated_data["advskew"] = $advskew; + $this->validated_data["password"] = $password; + $this->validated_data["uniqid"] = uniqid(); + } + // Values specific to Proxy ARP and other + if (in_array($mode, array("proxyarp", "other")) and $noexpand) { + $this->validated_data["noexpand"] = ""; + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc new file mode 100644 index 000000000..9f76d4358 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc @@ -0,0 +1,37 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-virtualipaddress-edit"]; + } + + public function action() { + global $config; + $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging + $change_note = " Deleted virtual IP via API"; // Add a change note + $del_vip = $config["virtualip"]["vip"][$this->validated_data["id"]]; // Save the virtual IP we are deleting + APITools\bring_down_virtual_ip($del_vip, $this->validated_data["id"]); // Bring down v IP and delete it + write_config(sprintf(gettext($change_note))); // Apply our configuration change + send_event("filter reload"); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $del_vip); + } + + public function validate_payload() { + global $config; + if (isset($this->initial_data['id'])) { + // Check that our rule ID exists + if (array_key_exists($this->initial_data["id"], $config["virtualip"]["vip"])) { + $this->validated_data["id"] = $this->initial_data['id']; + } else { + $this->errors[] = APIResponse\get(4018); + } + } else { + $this->errors[] = APIResponse\get(4017); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarpModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarpModify.inc index 77df3679c..fee8121f3 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarpModify.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarpModify.inc @@ -9,10 +9,9 @@ class APIStatusCarpModify extends APIBaseModel { parent::__construct(); $this->privileges = ["page-all", "page-status-carp"]; $this->methods = ["POST"]; - $this->validators = [$this->validate_payload()]; } - private function validate_payload() { + public function validate_payload() { // Check if user specified enable value if (isset($this->initial_data['enable'])) { // Check if value is true or false diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc index 9bda5c703..6d70eb3ac 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc @@ -6,7 +6,6 @@ require_once("api/framework/APIAuth.inc"); class APIBaseModel { public $client; public $privileges; - public $validators; public $initial_data; public $validated_data; public $errors; @@ -20,7 +19,6 @@ class APIBaseModel { $this->client = null; $this->requires_auth = true; $this->set_auth_mode = null; - $this->validators = []; $this->initial_data = APITools\get_request_data(); $this->validated_data = []; $this->errors = []; @@ -34,6 +32,12 @@ class APIBaseModel { return APIResponse\get(10); } + public function validate_payload() { + # This function is intended to be overridden by an API model extended class + # Any payload validation must be specified here + # If this class is not overridden no validation takes place + } + public function validate() { $this->check_enable(); $this->check_server_ip(); @@ -43,8 +47,7 @@ class APIBaseModel { $this->check_authentication(); $this->check_authorization(); } - $this->errors = array_filter(array_merge($this->errors, $this->validators)); - + $this->validate_payload(); if (count($this->errors) === 0) { return true; diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc index a99f47dbd..ae2283e01 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc @@ -70,7 +70,194 @@ function get($id, $data=[]) { "code" => "500", "return" => $id, "message" => "Your API request was valid but no actions were specified for this endpoint", - ] + ], + + 4000 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Port forward interface required" + ], + 4001=> [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Port forward rule protocol required" + ], + 4002 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Port forward redirect IP required" + ], + 4003 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Port forward redirect port required" + ], + 4004 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Port forward source required" + ], + 4005 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Port forward destination required" + ], + 4006=> [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown port forward interface" + ], + 4007 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown port forward protocol" + ], + 4008=> [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown port forward NAT reflection value" + ], + 4009 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid port forward redirect IP" + ], + 4010 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid port forward redirect port" + ], + 4011 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid port forward source address" + ], + 4012 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid port forward destination address" + ], + 4013 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid port forward source port" + ], + 4014 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid port forward destination port" + ], + 4015 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Port forward rule ID required" + ], + 4016 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Port forward rule ID does not exist" + ], + 4017 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Virtual IP configuration ID required" + ], + 4018 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Virtual IP configuration ID does not exist" + ], + 4019 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Virtual IP mode required" + ], + 4020 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Virtual IP interface required" + ], + 4021 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Virtual IP subnet required" + ], + 4022 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Virtual IP CARP password required" + ], + 4023 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown virtual IP mode" + ], + 4024 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown virtual IP interface" + ], + 4025 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid virtual IP subnet" + ], + 4026 =>[ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Virtual IP subnet already in use" + ], + 4027 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Virtual IP VHID already in use" + ], + 4028 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid virtual IP VHID" + ], + 4029 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid virtual IP CARP advertisement base" + ], + 4030 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid virtual IP CARP advertisement skew" + ], ]; $response = $responses[$id]; diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index 84fedf21c..123a2e1bd 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -252,4 +252,208 @@ function enable_carp($enable) { interfaces_sync_setup(); set_single_sysctl('net.inet.carp.allow', '1'); } +} + +// Sorts nat rules by specified criteria and reloads the filter +function sort_nat_rules($mode=null, $data=null) { + // Variables + global $config; + $sort_arr = []; + $master_arr = []; + foreach ($config["nat"]["rule"] as $idx => $fre) { + $curr_iface = $fre["interface"]; // Save our current entries interface + // Create our interface array if does not exist + if (!isset($sort_arr[$curr_iface])) { + $sort_arr[$curr_iface] = []; + } + // Check if user requested this rule to be placed at the top of array + if ($mode === "top" and $idx === $data) { + array_unshift($sort_arr[$curr_iface], $fre); + } else { + $sort_arr[$curr_iface][] = $fre; + } + } + foreach ($sort_arr as $if) { + foreach ($if as $rule) { + $master_arr[] = $rule; + } + } + $config["nat"]["rule"] = $master_arr; +} + +// Input a physical interface ID, or a descriptive interface name, and return the pfSense interface ID (lan,wan,optx) +function get_pfsense_if_id($interface) +{ + // Variables + global $err_lib, $config; + // Loop through our config and check each interface for a physical ID match + foreach ($config["interfaces"] as $ifid => $ife) { + // Check that we have a configured interface ID first + if (array_key_exists("if", $ife)) { + // Check if the interface id matches + if ($interface === $ife["if"]) { + return $ifid; // Return our pfSense interface ID + break; // Break loop just in case + } + } + // Check that we have a configured interface descr first + if (array_key_exists("descr", $ife)) { + // Check if the interface descr matches + if ($interface === $ife["descr"]) { + return $ifid; // Return our pfSense interface ID + break; // Break loop just in case + } + } else { + // Check if the pfSense interface ID matches + if (strtolower($interface) === $ifid) { + return $ifid; // Return our pfSense interface ID + break; // Break loop just in case + } + } + } +} + +// Check if input is valid for rule source and destination +function is_valid_rule_addr($addr, $direction) { + // Variables + $addr_types = array("any", "pppoe", "l2tp"); // Array of special src/dst types + $ret_val = array("valid" => true, "data" => array()); + // Check if our source values are valid + if (is_string($addr)) { + // Check src/dst is negated + if (str_starts_with("!", $addr)) { + $addr_not = true; + $addr = str_replace("!", "", $addr); + } + // Check if our source data is valid + $addr_if = str_replace("ip", "", $addr); // Save seperate variable to check for interface sourcees + if (is_ipaddr($addr) or is_subnet($addr)) { + $ret_val["data"] = array($direction => array("address" => $addr)); + } elseif (is_alias($addr)) { + $ret_val["data"] = array($direction => array("address" => $addr)); + } elseif (get_pfsense_if_id($addr_if)) { + $addr_pfif = get_pfsense_if_id($addr_if); // Save our interface pfid + // If source was interface address (ending in ip), otherwise assume entire subnet + if (str_replace($addr_if, "", $addr) === "ip") { + $ret_val["data"] = array($direction => array("network" => $addr_pfif . "ip")); + } else { + $ret_val["data"] = array($direction => array("network" => $addr_pfif)); + } + } elseif (in_array($addr, $addr_types)) { + if ($addr === "any") { + $ret_val["data"] = array($direction => array("any" => "")); + } else { + $ret_val["data"] = array($direction => array("network" => $addr)); + } + } else { + $ret_val["valid"] = false; + } + // If source is negated, add not to our array + if ($addr_not) { + $ret_val["data"][$direction]["not"] = ""; + } + } else { + $ret_val["valid"] = false; + } + return $ret_val; +} + +// Checks if a given string starts with another given string +function str_starts_with($needle, $haystack) { + $length = strlen($needle); + return (substr($haystack, 0, $length) === $needle); +} + +// Remove virtual IP and bring down virtual interface +function bring_down_virtual_ip($vip_ent, $id) { + global $err_lib, $config; + if ($vip_ent['mode'] == "proxyarp") { + $viface = $vip_ent['interface']; + unset($config["virtualip"]["vip"][$id]); + interface_proxyarp_configure($viface); + } else { + interface_vip_bring_down($vip_ent); + unset($config["virtualip"]["vip"][$id]); + } + if (count($config['virtualip']['vip']) == 0) { + unset($config['virtualip']['vip']); + } +} + +// Apply changes to virtual IPs +function apply_virtual_ip($vip_ent) { + $check_carp = false; + if (is_array($vip_ent)) { + foreach ($vip_ent as $vid => $ovip) { + if (!empty($ovip)) { + interface_vip_bring_down($ovip); + } + if ($vip_ent) { + switch ($vip_ent['mode']) { + case "ipalias": + interface_ipalias_configure($vip_ent); + break; + case "proxyarp": + interface_proxyarp_configure($vip_ent['interface']); + break; + case "carp": + $check_carp = true; + interface_carp_configure($vip_ent); + break; + default: + break; + } + } + } + } + /* Before changing check #4633 */ + if ($check_carp === true && !get_carp_status()) { + set_single_sysctl("net.inet.carp.allow", "1"); + } + filter_configure(); +} + +// Find an available virtual IP vhid +function next_vhid() { + global $err_lib, $config; + $vhid_config = $config["virtualip"]["vip"]; + $ret_vhid = null; + # Loop through our range of valid VHID ids + foreach (range(1, 255) as $idx) { + $vhid_exists = false; + # Loop through our virutal IPs and check if this VHID already exists + foreach ($vhid_config as $vip) { + if (intval($vip["vhid"]) === intval($idx)) { + $vhid_exists = true; + } + } + // Check if our VHID was already used + if (!$vhid_exists) { + $ret_vhid = $idx; + break; + } + } + return $ret_vhid; +} + +// Checks if an IP is in use elsewhere in our configuration +function is_ip_in_use($ip) { + global $err_lib, $config; + $vip_conf = $config["virtualip"]["vip"]; + $if_conf = $config["interfaces"]; + // Check if IP is used in our virtual IP configuration + foreach ($vip_conf as $vip) { + if ($ip === $vip["subnet"]) { + return true; + } + } + // Check if IP is used in our interface configuration + foreach ($if_conf as $iface) { + if ($ip === $iface["ipaddr"]) { + return true; + } elseif ($ip === $iface["ipaddrv6"]) { + return true; + } + } + return false; } \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/apicalls.inc b/pfSense-pkg-API/files/etc/inc/apicalls.inc index 0ec3026c4..e14153b60 100644 --- a/pfSense-pkg-API/files/etc/inc/apicalls.inc +++ b/pfSense-pkg-API/files/etc/inc/apicalls.inc @@ -192,7 +192,7 @@ function api_firewall_nat_portforwards() { function api_firewall_nat_portforwards_add() { # VARIABLES - global $err_lib, $g, $config, $tracker, $api_resp, $client_id, $client_params; + global $err_lib, $config, $api_resp, $client_id, $client_params; $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client $read_only_action = false; // Set whether this action requires read only access $req_privs = array("page-all", "page-firewall-nat-portforward-edit"); // Array of privileges allowing this action diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/add/index.php index 218b6cdef..e6cd07b76 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/add/index.php @@ -1,10 +1,5 @@ listen(); diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/delete/index.php index b91b475af..1d9050b61 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/delete/index.php @@ -1,10 +1,4 @@ listen(); diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/index.php index 0be936333..d08c13e7f 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/nat/portforwards/index.php @@ -1,10 +1,5 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/index.php index abb636394..6bfe12733 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/add/index.php index 4d3050f6c..bce33281e 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/add/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/delete/index.php index 24f6bf5fb..19b284171 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/index.php index 6ec28d0c3..bbaa8f63e 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/virtualips/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file From 93733a5e5883b64108ff71f04c7006a81f86ca79 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Sat, 22 Aug 2020 19:36:09 -0600 Subject: [PATCH 09/16] Converting alias endpoints from function based to object oriented --- CONTRIBUTING.md | 1 + .../inc/api/api_models/APIFirewallAliases.inc | 24 ++ .../api_models/APIFirewallAliasesDelete.inc | 58 ++++ .../APIFirewallNatPortForwardsAdd.inc | 3 +- .../api/api_models/APIFirewallRulesAdd.inc | 227 +++++++++++++++ .../api/api_models/APIFirewallRulesDelete.inc | 39 +++ .../files/etc/inc/api/framework/APIAuth.inc | 2 + .../etc/inc/api/framework/APIResponse.inc | 258 ++++++++++++++++++ .../files/etc/inc/api/framework/APITools.inc | 126 +++++++++ .../api/v1/firewall/aliases/delete/index.php | 10 +- .../www/api/v1/firewall/aliases/index.php | 10 +- .../www/api/v1/firewall/rules/add/index.php | 10 +- .../api/v1/firewall/rules/delete/index.php | 10 +- 13 files changed, 744 insertions(+), 34 deletions(-) create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliases.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesDelete.inc diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index f2be02dfa..32f6ba6f4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -228,6 +228,7 @@ If for any reason you need to access client data from within your API model clas property. This is an APIAuth object that contains details about the client: - `$this->client->username` : Our client's corresponding pfSense username +- `$this->client->ip_address` : The IP address of our client - `$this->client->is_authenticated` : Whether the client successfully authenticated. Keep in mind the APIBaseModel will handle all authentication and authorization for you, so this is likely unneeded. - `$this->client->is_authorized` : Whether the client was authorized. Keep in mind the APIBaseModel will diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliases.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliases.inc new file mode 100644 index 000000000..0299bd25c --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliases.inc @@ -0,0 +1,24 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-firewall-aliases"]; + } + + public function action() { + global $config; + // Check that we have a configuration + if (!empty($config["aliases"]["alias"])) { + $alias_array = $config["aliases"]["alias"]; + } else { + $alias_array = []; + } + return APIResponse\get(0, $alias_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDelete.inc new file mode 100644 index 000000000..7dd5c901e --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDelete.inc @@ -0,0 +1,58 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-aliases-edit"]; + } + + public function action() { + global $config; + $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging + $change_note = " Deleted firewall alias via API"; // Add a change note + $del_conf = $config["aliases"]["alias"][$this->validated_data["id"]]; // Capture alias config before deleting + unset($config["aliases"]["alias"][$this->validated_data["id"]]); // Remove this alias from our configuration + $config["aliases"]["alias"] = array_values($config["aliases"]["alias"]); // Reindex array + write_config(sprintf(gettext($change_note))); // Apply our configuration change + send_event("filter reload"); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $del_conf); + } + + public function validate_payload() { + global $config; + if (isset($this->initial_data['name'])) { + $name = $this->initial_data['name']; + $name = APITools\sanitize_str($name); + + // Check that alias is not in use in our configuration + if (!APITools\alias_in_use($name)) { + // Loop through our current config and find the index ID for our alias to delete + $c_count = 0; // Init loop counter + foreach ($config["aliases"]["alias"] as $ce) { + // Check if this entry matches our requested value + if ($ce["name"] === $name) { + $del_index = $c_count; + break; + } + $c_count++; + } + + if (is_numeric($del_index)) { + $this->validated_data["id"] = $del_index; + } else { + $this->errors[] = APIResponse\get(4055); + } + + } else { + $this->errors[] = APIResponse\get(4051); + } + } else { + $this->errors = APIResponse\get(4050); + } + + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc index 210264d77..ed66e453e 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc @@ -24,8 +24,7 @@ class APIFirewallNatPortForwardsAdd extends APIBaseModel { // TODO: break this down into smaller field specific validators public function validate_payload() { - global $config; - $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client + $user_created_msg = $this->client->username."@".$this->client->ip_address." (API)"; // Save the username and ip of client $allowed_nat_ref = ["enable", "disable", "purenat"]; // Save our allow NAT reflection types $allowed_prot = ["tcp", "udp", "tcp/udp", "icmp", "esp", "ah", "gre", "ipv6", "igmp", "pim", "ospf"]; if (isset($this->initial_data['interface'])) { diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesAdd.inc new file mode 100644 index 000000000..166faceed --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesAdd.inc @@ -0,0 +1,227 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-rules-edit"]; + } + + public function action() { + global $config; + $next_rule_id = count($config["filter"]["rule"]); // Save our next rule ID + $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging + $change_note = " Added firewall rule via API"; // Add a change note + $config["filter"]["rule"][] = $this->validated_data; // Write to our master config + APITools\sort_firewall_rules($this->initial_data["top"], $next_rule_id); // Sort our firewall rules + write_config(sprintf(gettext($change_note))); // Apply our configuration change + send_event("filter reload"); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $this->validated_data); + } + + // TODO: break this down into smaller field specific validators + public function validate_payload() { + global $config; + $user_created_msg = $this->client->username."@".$this->client->ip_address." (API)"; + $allowed_rule_types = array("pass", "block", "reject"); // Array of allowed rule types + $allowed_ip_prot = array("inet", "inet6", "inet46"); // Array of allowed IP protocols + $allowed_prot = array( + "any", + "tcp", + "udp", + "tcp/udp", + "icmp", + "esp", + "ah", + "gre", + "ipv6", + "igmp", + "pim", + "ospf", + "carp", + "pfsync" + ); + // Array of allowed ICMP subtypes + $icmp_subtypes = array( + "althost", + "dataconv", + "echorep", + "echoreq", + "inforep", + "inforeq", + "ipv6-here", + "ipv6-where", + "maskrep", + "maskreq", + "mobredir", + "mobregrep", + "mobregreq", + "paramprob", + "photuris", + "redir", + "routeradv", + "routersol", + "skip", + "squench", + "timerep", + "timereq", + "timex", + "trace", + "unreach" + ); + if (isset($this->initial_data['type'])) { + $type = $this->initial_data['type']; + } else { + $this->errors[] = APIResponse\get(4033); + } + if (isset($this->initial_data['interface'])) { + $interface = $this->initial_data['interface']; + $interface = APITools\get_pfsense_if_id($interface); + } else { + $this->errors[] = APIResponse\get(4034); + + } + if (isset($this->initial_data['ipprotocol'])) { + $ipprotocol = $this->initial_data['ipprotocol']; + } else { + $this->errors[] = APIResponse\get(4035); + } + if (isset($this->initial_data['protocol'])) { + $protocol = $this->initial_data['protocol']; + } else { + $this->errors[] = APIResponse\get(4036); + } + if (isset($this->initial_data['src'])) { + $src = $this->initial_data['src']; + } else { + $this->errors[] = APIResponse\get(4037); + } + if (isset($this->initial_data['srcport'])) { + $srcport = $this->initial_data['srcport']; + } + if (isset($this->initial_data['dst'])) { + $dst = $this->initial_data['dst']; + } else { + $this->errors[] = APIResponse\get(4038); + } + if (isset($this->initial_data['dstport'])) { + $dstport = $this->initial_data['dstport']; + } + if (isset($this->initial_data['icmptype'])) { + $icmp_type = $this->initial_data['icmptype']; + if (!is_array($icmp_type)) { + $icmp_type = array($icmp_type); + } + } + if (isset($this->initial_data['gateway'])) { + $gateway = $this->initial_data['gateway']; + } + if (isset($this->initial_data['disabled'])) { + $disabled = true; + } + if (isset($this->initial_data['descr'])) { + $descr = $this->initial_data['descr']; + } + if (isset($this->initial_data['log'])) { + $log = true; + } + if (isset($this->initial_data['top'])) { + $this->initial_data['top'] = "top"; + } + // INPUT VALIDATION/FORMATTING + // Check that our required array/interface values are valid + if (!in_array($type, $allowed_rule_types)) { + $this->errors[] = APIResponse\get(4039); + } elseif (!is_string($interface)) { + $this->errors[] = APIResponse\get(4040); + } elseif (!in_array($ipprotocol, $allowed_ip_prot)) { + $this->errors[] = APIResponse\get(4041); + } elseif (!in_array($protocol, $allowed_prot)) { + $this->errors[] = APIResponse\get(4042); + } elseif (isset($gateway) and !APITools\is_gateway($gateway)) { + $this->errors[] = APIResponse\get(4043); + } + // Check if rule is not disabled + if (!$disabled) { + $this->validated_data["id"] = ""; + } + // Check if logging is enabled + if ($log) { + $this->validated_data["log"] = ""; + } + // Check if gateway was specified + if (isset($gateway)) { + $this->validated_data["gateway"] = $gateway; + } + $this->validated_data["type"] = $type; + $this->validated_data["interface"] = $interface; + $this->validated_data["ipprotocol"] = $ipprotocol; + $this->validated_data["source"] = array(); + $this->validated_data["destination"] = array(); + $this->validated_data["descr"] = $descr; + $this->validated_data["created"] = array("time" => time(), "username" => $user_created_msg); + $this->validated_data["updated"] = $this->validated_data["created"]; + // Save our protocol if it is not 'any' + if ($protocol !== "any") { + $this->validated_data["protocol"] = $protocol; + } + // Add logging to config if enabled + if ($log) { + $this->validated_data["log"] = ""; + } + // Check if our source and destination values are valid + foreach (array("source" => $src, "destination" => $dst) as $dir => $val) { + $dir_check = APITools\is_valid_rule_addr($val, $dir); + if (!$dir_check["valid"] === true) { + $input_err = true; + if ($dir === "source") { + $this->errors[] = APIResponse\get(4044); + } else { + $this->errors[] = APIResponse\get(4045); + } + } else { + $this->validated_data = array_merge($this->validated_data, $dir_check["data"]); + } + } + // Check if protocol calls for additional specifications + if (in_array($protocol, array("tcp", "udp", "tcp/udp"))) { + $port_req = true; + } elseif ($protocol === "icmp") { + // Check if user specified ICMP subtypes + if (is_array($icmp_type)) { + // Loop through each of our subtypes + foreach ($icmp_type as $ict) { + if (!in_array($ict, $icmp_subtypes)) { + $this->errors[] = APIResponse\get(4046); + } + } + // Write our ICMP subtype config + $this->validated_data["icmptype"] = implode(",", $icmp_type); + } + } + // Check our src and dst port values if ports are required + if ($port_req) { + if (!isset($srcport) or !isset($dstport)) { + $this->errors[] = APIResponse\get(4047); + + } + foreach (array("source" => $srcport, "destination" => $dstport) as $dir => $val) { + $val = str_replace("-", ":", $val); + if (!is_port_or_range($val) and $val !== "any") { + $input_err = true; + if ($dir === "source") { + $this->errors[] = APIResponse\get(4048); + + } else { + $this->errors[] = APIResponse\get(4049); + } + } elseif ($val !== "any") { + $this->validated_data[$dir]["port"] = str_replace(":", "-", $val);; + } + } + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesDelete.inc new file mode 100644 index 000000000..7ae905e7b --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesDelete.inc @@ -0,0 +1,39 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-rules-edit"]; + } + + public function action() { + global $config; + $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging + $change_note = " Deleted firewall rule via API"; // Add a change note + $del_rule = $config["filter"]["rule"][$this->validated_data["id"]]; // Save the rule we are deleting + unset($config["filter"]["rule"][$this->validated_data["id"]]); // Remove rule from our config + APITools\sort_firewall_rules(); // Sort our firewall rules + write_config(sprintf(gettext($change_note))); // Apply our configuration change + send_event("filter reload"); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $del_rule); + } + + public function validate_payload() { + global $config; + if (isset($this->initial_data['id'])) { + // Check that our rule ID exists + if (array_key_exists($this->initial_data["id"], $config["filter"]["rule"])) { + $this->validated_data["id"] = $this->initial_data['id']; + } else { + $this->errors[] = APIResponse\get(4032); + } + } else { + $this->errors[] = APIResponse\get(4031); + } + + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc index f7ff708c2..fb6464048 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIAuth.inc @@ -9,6 +9,7 @@ class APIAuth { public $req_privs; public $username; public $privs; + public $ip_address; public $is_authenticated; public $is_authorized; @@ -19,6 +20,7 @@ class APIAuth { $this->request = APITools\get_request_data(); $this->req_privs = $req_privs; $this->privs = []; + $this->ip_address = $_SERVER["REMOTE_ADDR"]; $this->is_authenticated = $this->authenticate(); $this->is_authorized = $this->authorize(); } diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc index ae2283e01..9715c894c 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc @@ -258,6 +258,264 @@ function get($id, $data=[]) { "return" => $id, "message" => "Invalid virtual IP CARP advertisement skew" ], + 4031 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule ID required" + ], + 4032 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule ID does not exist" + ], + 4033 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule type required" + ], + 4034 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule interface required" + ], + 4035 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule IP version required" + ], + 4036 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule protocol required" + ], + 4037 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule source required" + ], + 4038 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule destination required" + ], + 4039 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown firewall rule type" + ], + 4040 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown firewall rule interface" + ], + 4041 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown firewall rule IP version" + ], + 4042 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown firewall rule protocol" + ], + 4043 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown firewall rule gateway" + ], + 4044 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall rule source" + ], + 4045 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall rule destination" + ], + 4046 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown firewall rule ICMP subtype" + ], + 4047 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall rule source and destination port required" + ], + 4048 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall rule source port" + ], + 4049 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall rule destination port" + ], + 4050 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias name required" + ], + 4051 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias in use" + ], + 4052 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias address value required" + ], + 4053 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias name must be type string" + ], + 4054 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias address must be type array" + ], + 4055 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias does not exist" + ], + 4056 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias already exists" + ], + 4057 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown firewall alias type" + ], + 4058 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall alias IP address or hostname" + ], + 4059 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall alias subnet" + ], + 4060 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall alias port" + ], + 4061 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias type required" + ], + 4062 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias type must be type string" + ], + 4063 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias description must be type string" + ], + 4064 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias detail must be type array" + ], + 4065 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall alias port value" + ], + 4066 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias port values must be numeric" + ], + 4067 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall alias IP address" + ], + 4068 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall alias subnet" + ], + 4069 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall alias address values must be valid CIDR" + ], + 4070 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Alias address contents must be type string" + ], + 4071 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Alias detail contents must be type string" + ], + 4072 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Firewall state maximum size required" + ], + 4073 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid firewall state table size" + ], ]; $response = $responses[$id]; diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index 123a2e1bd..40a7adf34 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -456,4 +456,130 @@ function is_ip_in_use($ip) { } } return false; +} + +// Sorts filter rules by specified criteria and reloads the filter +function sort_firewall_rules($mode=null, $data=null) { + // Variables + global $config; + $sort_arr = []; + $master_arr = []; + foreach ($config["filter"]["rule"] as $idx => $fre) { + $curr_iface = $fre["interface"]; // Save our current entries interface + // Create our interface array if does not exist + if (!isset($sort_arr[$curr_iface])) { + $sort_arr[$curr_iface] = []; + } + // Check if user requested this rule to be placed at the top of array + if ($mode === "top" and $idx === $data) { + array_unshift($sort_arr[$curr_iface], $fre); + } else { + $sort_arr[$curr_iface][] = $fre; + } + } + foreach ($sort_arr as $if) { + foreach ($if as $rule) { + $master_arr[] = $rule; + } + } + $config["filter"]["rule"] = $master_arr; +} + +// Checks if inputted routing gateway exists +function is_gateway($gw) { + // Local variables + global $config; + $gw_config = $config["gateways"]["gateway_item"]; + $gw_exists = false; + // Check that we have gateways configured + if (is_array($gw_config)) { + // Loop through each gateway and see if name matches + foreach ($gw_config as $gw_item) { + if ($gw === $gw_item["name"]) { + $gw_exists = true; + break; + } + } + } + return $gw_exists; +} + +// Duplicate function from /firewall_aliases.php: accept input and check if alias exists +function alias_find_references($section, $field, $origname, &$is_alias_referenced, &$referenced_by) { + global $err_lib, $config; + if (!$origname || $is_alias_referenced) { + return; + } + $sectionref = &$config; + foreach ($section as $sectionname) { + if (is_array($sectionref) && isset($sectionref[$sectionname])) { + $sectionref = &$sectionref[$sectionname]; + } else { + return; + } + } + if (is_array($sectionref)) { + foreach ($sectionref as $itemkey => $item) { + $fieldfound = true; + $fieldref = &$sectionref[$itemkey]; + foreach ($field as $fieldname) { + if (is_array($fieldref) && isset($fieldref[$fieldname])) { + $fieldref = &$fieldref[$fieldname]; + } else { + $fieldfound = false; + break; + } + } + if ($fieldfound && $fieldref == $origname) { + $is_alias_referenced = true; + if (is_array($item)) { + $referenced_by = $item['descr']; + } + break; + } + } + } +} + +// Input an alias name and check if the alias exists +function alias_in_use($alias_name) { + $is_alias_referenced = false; + $referenced_by = false; + // Firewall rules + alias_find_references(array('filter', 'rule'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('filter', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('filter', 'rule'), array('source', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('filter', 'rule'), array('destination', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT Rules + alias_find_references(array('nat', 'rule'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'rule'), array('source', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'rule'), array('destination', 'port'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'rule'), array('target'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'rule'), array('local-port'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT 1:1 Rules + //alias_find_references(array('nat', 'onetoone'), array('external'), $alias_name, $is_alias_referenced, $referenced_by); + //alias_find_references(array('nat', 'onetoone'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'onetoone'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + // NAT Outbound Rules + alias_find_references(array('nat', 'outbound', 'rule'), array('source', 'network'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'outbound', 'rule'), array('sourceport'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'outbound', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'outbound', 'rule'), array('dstport'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('nat', 'outbound', 'rule'), array('target'), $alias_name, $is_alias_referenced, $referenced_by); + // Alias in an alias + alias_find_references(array('aliases', 'alias'), array('address'), $alias_name, $is_alias_referenced, $referenced_by); + // Load Balancer + alias_find_references(array('load_balancer', 'lbpool'), array('port'), $alias_name, $is_alias_referenced, $referenced_by); + alias_find_references(array('load_balancer', 'virtual_server'), array('port'), $alias_name, $is_alias_referenced, $referenced_by); + // Static routes + alias_find_references(array('staticroutes', 'route'), array('network'), $alias_name, $is_alias_referenced, $referenced_by); + return $is_alias_referenced; +} + +// Strip special characters and replace whitespace with underscore +function sanitize_str($string) { + $string = str_replace(' ', '_', $string); // Replace whitespace with underscore + $string = preg_replace('/[^A-Za-z0-9\-_.]/', '', $string); // Remove special characters + return $string; } \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/delete/index.php index f8b65c7ee..a0c42df5c 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/delete/index.php @@ -1,10 +1,4 @@ listen(); diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/index.php index 213952e5e..2e2792383 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/add/index.php index b587259bc..ef97ffa9e 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/add/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/delete/index.php index 384d06495..638cfe349 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/rules/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file From 3c7a9346a7068e034a0af05a93e9a79e91ebbb31 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Mon, 24 Aug 2020 08:18:40 -0600 Subject: [PATCH 10/16] Converted more endpoints to object-oriented structure, minor adjustments to framework and contributor notes --- CONTRIBUTING.md | 28 ++- .../inc/api/api_models/APIFirewallAliases.inc | 6 +- .../api/api_models/APIFirewallAliasesAdd.inc | 165 ++++++++++++++ .../APIFirewallAliasesAddAddress.inc | 146 ++++++++++++ .../api_models/APIFirewallAliasesDelete.inc | 16 +- .../APIFirewallAliasesDeleteAddress.inc | 73 ++++++ .../api_models/APIFirewallAliasesModify.inc | 116 ++++++++++ .../etc/inc/api/api_models/APIFirewallNat.inc | 6 +- .../api_models/APIFirewallNatPortForwards.inc | 6 +- .../APIFirewallNatPortForwardsAdd.inc | 11 +- .../APIFirewallNatPortForwardsDelete.inc | 14 +- .../inc/api/api_models/APIFirewallRules.inc | 6 +- .../api/api_models/APIFirewallRulesAdd.inc | 13 +- .../api/api_models/APIFirewallRulesDelete.inc | 14 +- .../inc/api/api_models/APIFirewallStates.inc | 17 ++ .../api/api_models/APIFirewallStatesSize.inc | 28 +++ .../APIFirewallStatesSizeModify.inc | 44 ++++ .../api/api_models/APIFirewallVirtualIPs.inc | 6 +- .../api_models/APIFirewallVirtualIPsAdd.inc | 14 +- .../APIFirewallVirtualIPsDelete.inc | 12 +- .../etc/inc/api/api_models/APIStatusCarp.inc | 4 +- .../etc/inc/api/api_models/APISystemAPI.inc | 16 ++ .../etc/inc/api/api_models/APISystemARP.inc | 17 ++ .../inc/api/api_models/APISystemARPDelete.inc | 28 +++ .../api/api_models/APISystemCertificates.inc | 24 ++ .../api_models/APISystemCertificatesAdd.inc | 55 +++++ .../APISystemCertificatesDelete.inc | 55 +++++ .../inc/api/api_models/APISystemConfig.inc | 18 ++ .../etc/inc/api/api_models/APISystemDNS.inc | 34 +++ .../api_models/APISystemDNSDeleteServers.inc | 50 ++++ .../inc/api/api_models/APISystemDNSModify.inc | 60 +++++ .../inc/api/api_models/APISystemHostname.inc | 29 +++ .../api_models/APISystemHostnameModify.inc | 55 +++++ .../inc/api/api_models/APISystemVersion.inc | 17 ++ .../files/etc/inc/api/api_models/APIUsers.inc | 16 ++ .../etc/inc/api/api_models/APIUsersDelete.inc | 37 +++ .../api/api_models/APIUsersDeletePrivs.inc | 59 +++++ .../etc/inc/api/framework/APIBaseModel.inc | 11 + .../etc/inc/api/framework/APIResponse.inc | 214 +++++++++++++++++- .../files/etc/inc/api/framework/APITools.inc | 65 ++++++ .../v1/firewall/aliases/add/address/index.php | 10 +- .../www/api/v1/firewall/aliases/add/index.php | 10 +- .../firewall/aliases/delete/address/index.php | 10 +- .../api/v1/firewall/aliases/modify/index.php | 10 +- .../www/api/v1/firewall/states/index.php | 10 +- .../www/api/v1/firewall/states/size/index.php | 10 +- .../v1/firewall/states/size/modify/index.php | 10 +- .../usr/local/www/api/v1/system/api/index.php | 10 +- .../www/api/v1/system/arp/delete/index.php | 10 +- .../usr/local/www/api/v1/system/arp/index.php | 10 +- .../api/v1/system/certificates/add/index.php | 10 +- .../v1/system/certificates/delete/index.php | 10 +- .../www/api/v1/system/certificates/index.php | 10 +- .../local/www/api/v1/system/config/index.php | 10 +- .../v1/system/dns/delete/servers/index.php | 10 +- .../usr/local/www/api/v1/system/dns/index.php | 10 +- .../www/api/v1/system/dns/modify/index.php | 10 +- .../www/api/v1/system/hostname/index.php | 10 +- .../api/v1/system/hostname/modify/index.php | 10 +- .../local/www/api/v1/system/version/index.php | 10 +- .../local/www/api/v1/users/delete/index.php | 10 +- .../www/api/v1/users/delete/privs/index.php | 10 +- .../usr/local/www/api/v1/users/index.php | 10 +- 63 files changed, 1573 insertions(+), 262 deletions(-) create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesAddAddress.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDeleteAddress.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesModify.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStates.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStatesSize.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStatesSizeModify.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemAPI.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemARP.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemARPDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificates.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificatesAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificatesDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemConfig.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNS.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNSDeleteServers.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNSModify.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemHostname.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemHostnameModify.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemVersion.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsers.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDeletePrivs.inc diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 32f6ba6f4..433894cd4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -116,6 +116,10 @@ writing a model that tests user's local database credentials and do not want the auth mode you would specify `$this->set_auth_mode = "local";` to always force local authentication. Defaults to the API's configured auth mode in the /api/ webConfigurator page. +- `$this->change_note` : Sets the description of the action that occurred via API. This value will be shown in the +change logs found at Diagnostics > Backup & Restore > Config History. This defaults to "Made unknown change via API". +This is only necessary if your API model writes changes to the configuration. + #### Other Base Model Properties #### There are other properties inherited from APIBaseModel that are not intended (and shouldn't be) overridden by your @@ -125,6 +129,19 @@ custom API model: - `$this->initial_data` : The request data as it was when the object was created - `$this->validated_data` : An array for validators to use to populate data that has been validated - `$this->errors` : An array to populate any errors encountered. Should be an array of APIResponse values. +- `$this->config` : Our pfSense configuration array. You may read current configuration values using this array or write +changes to the configuration by updating it's values. If you do make changes to the configuration, you must use call +`$this->write_config()` to apply them. + +#### Reading and Writing to pfSense's XML Configuration #### +Included in the API framework are properties and methods to read and write to pfSense's XML configuration. Please note +that other functions are likely required to configure pfSense's backend to use the new configuration. These properties +and methods are available anywhere within your API model: + +- `$this->config` : Our pfSense XML configuration in a PHP array format. You may read the current configuration from +this property or update/add new configuration by assigning the corresponding array key new values +- `$this->write_config()` : This method writes any changes made to $this->config to pfSense's XML configuration file. +Any changes made to $this->config will not be applied until this method is executed. #### Overriding API Model Validation #### By default, API models do not provide any sort of validation. You are responsible for overriding the class method to @@ -159,13 +176,13 @@ class NewAPIModel extends APIBaseModel { } ``` - #### Writing API Model Action #### By default, the API model will return a 'Your API request was valid but no actions were specified for this endpoint' error after validating input. This is because we need to tell it what to do when our request is valid! We do this by overriding the APIBaseModel's `action()` method. This method should simply be a set of tasks to perform upon a successful API call. If you are writing an endpoint to perform a change that is usually performed via the pfSense -webConfigurator, it may be helpful to look at the PHP code for the webConfigurator page. +webConfigurator, it may be helpful to look at the PHP code for the webConfigurator page. This method must return an +APIResponse item. As a basic example, if I wanted to add our validated input to our pfSense configuration: @@ -194,16 +211,13 @@ class NewAPIModel extends APIBaseModel { # Tell our API model what to do after successfully validating the client's request public function action(){ - $_SESSION["Username"] = $this->client->username; // Save our user to session data for logging - $change_note = " Added TEST value via API"; // Add a change note - $config["test"][] = $this->validated_data; // Write a new 'test' item to our master config - write_config(sprintf(gettext($change_note))); // Apply our configuration change + $this->config["test"][] = $this->validated_data; // Write a new 'test' item to our master config + $this->write_config(); // Apply our configuration change return APIResponse\get(0, $this->validated_data); // Return a success response containing our added data } } ``` - #### Writing API endpoints #### API models are not useful if we do not have an API endpoint that calls upon the model. API endpoints are the PHP scripts that will be placed in pfSense's web path. To create a new API endpoint, add an index.php file in diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliases.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliases.inc index 0299bd25c..e545f4580 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliases.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliases.inc @@ -12,10 +12,10 @@ class APIFirewallAliases extends APIBaseModel { } public function action() { - global $config; + // Check that we have a configuration - if (!empty($config["aliases"]["alias"])) { - $alias_array = $config["aliases"]["alias"]; + if (!empty($this->config["aliases"]["alias"])) { + $alias_array = $this->config["aliases"]["alias"]; } else { $alias_array = []; } diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesAdd.inc new file mode 100644 index 000000000..bc234a1e4 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesAdd.inc @@ -0,0 +1,165 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-aliases-edit"]; + $this->change_note = "Added firewall alias via API"; + } + + public function action() { + // Add our alias + $this->config["aliases"] = !is_array($this->config["aliases"]) ? array("alias" => []) : $this->config["aliases"]; + $this->config["aliases"]["alias"][] = $this->validated_data; // Write our configuration change + $this->write_config(); // Apply our configuration change + send_event("filter reload"); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + + $allowed_alias_types = array("host", "network", "port"); // Array of allowed alias types + if (isset($this->initial_data['name'])) { + $name = $this->initial_data['name']; + $name = APITools\sanitize_str($name); + } else { + $this->errors[] = APIResponse\get(4050); + } + if (isset($this->initial_data['type'])) { + $type = $this->initial_data['type']; + $type = trim($type); + } else { + $this->errors[] = APIResponse\get(4061); + } + if (isset($this->initial_data['address'])) { + $address = $this->initial_data['address']; + // Convert string to array + if (!is_array($address)) { + $address = array($address); + } + } else { + $this->errors[] = APIResponse\get(4052); + + } + if (isset($this->initial_data['descr'])) { + $descr = $this->initial_data['descr']; + } + if (isset($this->initial_data['detail'])) { + $detail = $this->initial_data['detail']; + // Convert string to array + if (!is_array($detail)) { + $detail = array($detail); + } + } + // Check that our input is valid + if (!is_string($name)) { + $this->errors[] = APIResponse\get(4053); + } elseif (!is_string($type)) { + $this->errors[] = APIResponse\get(4062); + } elseif (!in_array($type, $allowed_alias_types)) { + $this->errors[] = APIResponse\get(4057); + } elseif (isset($descr) and !is_string($descr)) { + $this->errors[] = APIResponse\get(4063); + } elseif (!is_array($address)) { + $this->errors[] = APIResponse\get(4054); + } elseif (isset($detail) and !is_array($detail)) { + $this->errors[] = APIResponse\get(4063); + } + if (!isset($type_err)) { + // Loop through our arrays and ensure the values are valid + $a_count = 0; // Define a loop counter + foreach ($address as $ae) { + // Conditions for alias type 'port' + if ($type === "port") { + // Check that our value is numeric + if (is_numeric($ae)) { + if (1 <= intval($ae) and intval($ae) <= 65535) { + $address[$a_count] = strval($ae); + } else { + $this->errors[] = APIResponse\get(4065); + + } + } else { + $this->errors[] = APIResponse\get(4066); + + } + } + // Conditionals for alias type 'network' + if ($type === "network") { + // Check that values are strings + if (is_string($ae)) { + // Check that string is a network CIDR + if (strpos($ae, "/")) { + $net_ip = explode("/", $ae)[0]; // Save our network IP + $bit_mask = explode("/", $ae)[1]; // Save our subnet bit mask + // Check if our IP is IPv4 + if (is_ipaddrv4($net_ip)) { + $max_bits = 32; // Assign our maximum IPv4 bitmask + } elseif (is_ipaddrv6($net_ip)) { + $max_bits = 128; // Assign our maximum IPv4 bitmask + } else { + $this->errors[] = APIResponse\get(4067); + + } + // Check if our bitmask is numeric and in range + if (is_numeric($bit_mask)) { + if (1 <= intval($bit_mask) and intval($bit_mask) <= $max_bits) { + continue; + } else { + $this->errors[] = APIResponse\get(4068); + } + } else { + $this->errors[] = APIResponse\get(4069); + } + } else { + $this->errors[] = APIResponse\get(4069); + + } + } else { + $this->errors[] = APIResponse\get(4070); + + } + } + // Conditions for alias type 'host' + if ($type === "host") { + // Check that values are strings + if (is_string($ae)) { + $address[$a_count] = sanitize_str($ae); + } else { + $this->errors[] = APIResponse\get(4070); + + } + } + // Increase our counter + $a_count++; + } + // Check each of our alias details + foreach ($detail as $de) { + if (!is_string($de)) { + $this->errors[] = APIResponse\get(4071); + + } + } + } + // Check our existing aliases + if (is_array($this->config["aliases"])) { + $c_count = 0; + // Loop through each alias and see if alias already exists + foreach ($this->config["aliases"]["alias"] as $ce) { + if ($ce["name"] === $name) { + $this->errors[] = APIResponse\get(4056); + + } + } + } + $this->validated_data["name"] = $name; // Save our alias name + $this->validated_data["type"] = $type; // Save our type + $this->validated_data["descr"] = $descr; // Save our description + $this->validated_data["address"] = implode(" ", $address); // Join array in to space seperated string + $this->validated_data["detail"] = implode("||", $detail); // Join array in to || seperated string + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesAddAddress.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesAddAddress.inc new file mode 100644 index 000000000..ca39f5731 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesAddAddress.inc @@ -0,0 +1,146 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-aliases-edit"]; + $this->change_note = "Added firewall alias address via API"; + } + + public function action() { + // Add our alias + $this->config["aliases"]["alias"][$this->initial_data["id"]]["address"] = implode(" ", $this->validated_data["address"]); + $this->config["aliases"]["alias"][$this->initial_data["id"]]["detail"] = implode("||", $this->validated_data["detail"]); + $this->write_config(); // Apply our configuration change + send_event("filter reload"); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $this->config["aliases"]["alias"][$this->initial_data["id"]]); + } + + public function validate_payload() { + + if (isset($this->initial_data['name'])) { + $name = $this->initial_data['name']; + $name = APITools\sanitize_str($name); + } else { + $this->errors[] = APIResponse\get(4050); + } + if (isset($this->initial_data['address'])) { + $address = $this->initial_data['address']; + // Convert string to array + if (!is_array($address)) { + $address = array($address); + } + } else { + $this->errors[] = APIResponse\get(4052); + + } + if (isset($this->initial_data['detail'])) { + $detail = $this->initial_data['detail']; + // Convert string to array + if (!is_array($detail)) { + $detail = array($detail); + } + } + // Check that our input is valid + if (!is_string($name)) { + $this->errors[] = APIResponse\get(4053); + } elseif (!is_array($address)) { + $this->errors[] = APIResponse\get(4054); + } elseif (isset($detail) and !is_array($detail)) { + $this->errors[] = APIResponse\get(4064); + } + // Loop through our existing firewall entries and check for our requested alias + $c_count = 0; + foreach ($this->config["aliases"]["alias"] as $ce) { + if ($name === $ce["name"]) { + $this->initial_data["id"] = $c_count; + $type = $ce["type"]; + $curr_addr = explode(" ", $ce["address"]); + $curr_detail = explode("||", $ce["detail"]); + break; + } + $c_count++; // Increase our counter + } + // If we could not find an alias, return error + if (!isset($type)) { + $this->errors[] = APIResponse\get(4055); + } + if (!isset($type_err)) { + // Loop through our arrays and ensure the values are valid + $a_count = 0; // Define a loop counter + foreach ($address as $ae) { + // Conditions for alias type 'port' + if ($type === "port") { + // Check that our value is numeric + if (is_numeric($ae)) { + if (1 <= intval($ae) and intval($ae) <= 65535) { + $address[$a_count] = strval($ae); + } else { + $this->errors[] = APIResponse\get(4065); + + } + } else { + $this->errors[] = APIResponse\get(4066); + + } + } + // Conditionals for alias type 'network' + if ($type === "network") { + // Check that values are strings + if (is_string($ae)) { + // Check that string is a network CIDR + if (strpos($ae, "/")) { + $net_ip = explode("/", $ae)[0]; // Save our network IP + $bit_mask = explode("/", $ae)[1]; // Save our subnet bit mask + // Check if our IP is IPv4 + if (is_ipaddrv4($net_ip)) { + $max_bits = 32; // Assign our maximum IPv4 bitmask + } elseif (is_ipaddrv6($net_ip)) { + $max_bits = 128; // Assign our maximum IPv4 bitmask + } else { + $this->errors[] = APIResponse\get(4067); + } + // Check if our bitmask is numeric and in range + if (is_numeric($bit_mask)) { + if (1 <= intval($bit_mask) and intval($bit_mask) <= $max_bits) { + continue; + } else { + $this->errors[] = APIResponse\get(4068); + } + } else { + $this->errors[] = APIResponse\get(4069); + } + } else { + $this->errors[] = APIResponse\get(4069); + } + } else { + $this->errors[] = APIResponse\get(4070); + } + } + // Conditions for alias type 'host' + if ($type === "host") { + // Check that values are strings + if (is_string($ae)) { + $address[$a_count] = APITools\sanitize_str($ae); + } else { + $this->errors[] = APIResponse\get(4070); + } + } + // Increase our counter + $a_count++; + } + // Check each of our alias details + foreach ($detail as $de) { + if (!is_string($de)) { + $this->errors[] = APIResponse\get(4071); + } + } + $this->validated_data["address"] = array_merge($curr_addr, $address); + $this->validated_data["detail"] = array_merge($curr_detail, $detail); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDelete.inc index 7dd5c901e..67a8b3da8 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDelete.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDelete.inc @@ -8,22 +8,20 @@ class APIFirewallAliasesDelete extends APIBaseModel { parent::__construct(); $this->methods = ["POST"]; $this->privileges = ["page-all", "page-firewall-aliases-edit"]; + $this->change_note = "Deleted firewall alias via API"; } public function action() { - global $config; - $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging - $change_note = " Deleted firewall alias via API"; // Add a change note - $del_conf = $config["aliases"]["alias"][$this->validated_data["id"]]; // Capture alias config before deleting - unset($config["aliases"]["alias"][$this->validated_data["id"]]); // Remove this alias from our configuration - $config["aliases"]["alias"] = array_values($config["aliases"]["alias"]); // Reindex array - write_config(sprintf(gettext($change_note))); // Apply our configuration change + $del_conf = $this->config["aliases"]["alias"][$this->validated_data["id"]]; // Capture alias config before deleting + unset($this->config["aliases"]["alias"][$this->validated_data["id"]]); // Remove this alias from our configuration + $this->config["aliases"]["alias"] = array_values($this->config["aliases"]["alias"]); // Reindex array + $this->write_config(); // Apply our configuration change send_event("filter reload"); // Ensure our firewall filter is reloaded return APIResponse\get(0, $del_conf); } public function validate_payload() { - global $config; + if (isset($this->initial_data['name'])) { $name = $this->initial_data['name']; $name = APITools\sanitize_str($name); @@ -32,7 +30,7 @@ class APIFirewallAliasesDelete extends APIBaseModel { if (!APITools\alias_in_use($name)) { // Loop through our current config and find the index ID for our alias to delete $c_count = 0; // Init loop counter - foreach ($config["aliases"]["alias"] as $ce) { + foreach ($this->config["aliases"]["alias"] as $ce) { // Check if this entry matches our requested value if ($ce["name"] === $name) { $del_index = $c_count; diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDeleteAddress.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDeleteAddress.inc new file mode 100644 index 000000000..1a22cc7cc --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesDeleteAddress.inc @@ -0,0 +1,73 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-aliases-edit"]; + $this->change_note = "Deleted firewall alias address via API"; + } + + public function action() { + $this->config["aliases"]["alias"][$this->validated_data["id"]]["address"] = implode(" ", $this->validated_data["address"]); + $this->config["aliases"]["alias"][$this->validated_data["id"]]["detail"] = implode("||", $this->validated_data["detail"]); + $this->write_config(); // Apply our configuration change + send_event("filter reload"); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $this->config["aliases"]["alias"][$this->validated_data["id"]]); + } + + public function validate_payload() { + + if (isset($this->initial_data['name'])) { + $name = $this->initial_data['name']; + $name = APITools\sanitize_str($name); + } else { + $this->errors[] = APIResponse\get(4050); + } + if (isset($this->initial_data['address'])) { + $address = $this->initial_data['address']; + // Convert string to array + if (!is_array($address)) { + $address = array($address); + } + } else { + $this->errors[] = APIResponse\get(4052); + } + // Check that our input is valid + if (!is_string($name)) { + $this->errors[] = APIResponse\get(4053); + } elseif (!is_array($address)) { + $this->errors[] = APIResponse\get(4054); + } + // Loop through our existing firewall entries and check for our requested alias + $c_count = 0; + foreach ($this->config["aliases"]["alias"] as $ce) { + if ($name === $ce["name"]) { + $alias_found = true; + $this->validated_data["id"] = $c_count; + $this->validated_data["address"] = explode(" ", $ce["address"]); + $this->validated_data["detail"] = explode("||", $ce["detail"]); + break; + } + $c_count++; // Increase our counter + } + // If we could not find an alias, return error + if ($alias_found !== true) { + $this->errors[] = APIResponse\get(4055); + } + + // Loop through our existing configuration and remove alias address values on match + $r_count = 0; + foreach ($this->validated_data["address"] as $re) { + if (in_array($re, $address)) { + unset($this->validated_data["address"][$r_count]); + unset($this->validated_data["detail"][$r_count]); + } + $r_count++; // Increase our counter + } + + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesModify.inc new file mode 100644 index 000000000..673bfdce8 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallAliasesModify.inc @@ -0,0 +1,116 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-firewall-aliases-edit"]; + $this->change_note = "Modified firewall alias address via API"; + } + + public function action() { + $this->config["aliases"]["alias"][$this->initial_data["id"]] = $this->validated_data; // Write to our master config + $this->write_config(); // Apply our configuration change + send_event("filter reload"); // Ensure our firewall filter is reloaded + return APIResponse\get(0, $this->config["aliases"]["alias"][$this->initial_data["id"]]); + } + + public function validate_payload() { + + $allowed_alias_types = array("host", "network", "port"); // Array of allowed alias types + if (isset($this->initial_data['name'])) { + $name = $this->initial_data['name']; + $name = APITools\sanitize_str($name); + } else { + $this->errors[] = APIResponse\get(4050); + } + if (isset($this->initial_data["new_name"])) { + $new_name = $this->initial_data['new_name']; + $new_name = APITools\sanitize_str($new_name); + } + if (isset($this->initial_data['type'])) { + $type = $this->initial_data['type']; + $type = trim($type); + } else { + $type = alias_get_type($name); + } + if (isset($this->initial_data['address'])) { + $address = $this->initial_data['address']; + // Convert string to array + if (!is_array($address)) { + $address = array($address); + } + } + if (isset($this->initial_data['descr'])) { + $descr = strval($this->initial_data['descr']); + $descr = str_replace(PHP_EOL, "", $descr); + } + if (isset($this->initial_data['detail'])) { + $detail = $this->initial_data['detail']; + // Convert string to array + if (!is_array($detail)) { + $detail = array($detail); + } + } + // INPUT VALIDATION + if (!is_alias($name)) { + $this->errors[] = APIResponse\get(4055); + } elseif (isset($new_name) and is_alias($new_name)) { + $this->errors[] = APIResponse\get(4056); + } elseif (isset($type) and !in_array($type, $allowed_alias_types)) { + $this->errors[] = APIResponse\get(4057); + } + if (isset($address)) { + foreach ($address as $nae) { + if ($type === "host") { + if (!is_ipaddr($nae) and !is_hostname($nae)) { + $this->errors[] = APIResponse\get(4058); + break; + } + } + if ($type === "network") { + if (!is_subnet($nae) and !is_hostname($nae)) { + $this->errors[] = APIResponse\get(4059); + break; + } + } + if ($type === "port") { + if (!is_port_or_range($nae)) { + $this->errors[] = APIResponse\get(4060); + break; + } + } + } + } + // Save our alias ID + $c_count = 0; + foreach ($this->config["aliases"]["alias"] as $ca) { + if ($name === $ca["name"]) { + $this->initial_data["id"] = $c_count; + break; + } + $c_count++; + } + // Make our alias change + $this->validated_data = $this->config["aliases"]["alias"][$this->initial_data["id"]]; // Save our current alias entry + if (isset($new_name)) { + $this->validated_data["name"] = $new_name; + update_alias_name($new_name, $name); // Update our alias name + } + if (isset($type)) { + $this->validated_data["type"] = $type; + } + if (isset($descr)) { + $this->validated_data["descr"] = $descr; + } + if (isset($address)) { + $this->validated_data["address"] = implode(" ", $address); + } + if (isset($detail)) { + $this->validated_data["detail"] = implode("||", $detail); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNat.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNat.inc index 3c0d8b3ae..e93314fc6 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNat.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNat.inc @@ -12,10 +12,10 @@ class APIFirewallNat extends APIBaseModel { } public function action() { - global $config; + // Check that we have a NAT configuration - if (!empty($config["nat"])) { - $this->validated_data = $config["nat"]; + if (!empty($this->config["nat"])) { + $this->validated_data = $this->config["nat"]; } return APIResponse\get(0, $this->validated_data); } diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwards.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwards.inc index 6a0e91c07..72332840a 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwards.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwards.inc @@ -11,10 +11,10 @@ class APIFirewallNatPortForwards extends APIBaseModel { } public function action() { - global $config; + // Check that we have a virtual IP configuration - if (!empty($config["nat"]["rule"])) { - $this->validated_data = $config["nat"]["rule"]; + if (!empty($this->config["nat"]["rule"])) { + $this->validated_data = $this->config["nat"]["rule"]; } return APIResponse\get(0, $this->validated_data); } diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc index ed66e453e..54b5b7aff 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc @@ -7,17 +7,16 @@ class APIFirewallNatPortForwardsAdd extends APIBaseModel { public function __construct() { parent::__construct(); $this->methods = ["POST"]; + $this->change_note = "Added NAT rule via API"; $this->privileges = ["page-all", "page-firewall-nat-portforward-edit"]; } public function action() { - global $config; - $_SESSION["Username"] = $this->client->username; - $change_note = " Added NAT rule via API"; // Add a change note - $config["nat"]["rule"][] = $this->validated_data; // Write to our master config - $next_rule_id = count($config["nat"]["rule"]); // Save our next rule ID + + $this->config["nat"]["rule"][] = $this->validated_data; // Write to our master config + $next_rule_id = count($this->config["nat"]["rule"]); // Save our next rule ID APITools\sort_nat_rules($this->initial_data["top"], $next_rule_id); // Sort our nat rules - write_config(sprintf(gettext($change_note))); // Apply our configuration change + $this->write_config(); // Apply our configuration change filter_configure(); // Ensure our firewall filter is reloaded return APIResponse\get(0, $this->validated_data); } diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc index 9c6e9f576..8d75d600c 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc @@ -8,25 +8,23 @@ class APIFirewallNatPortForwardsDelete extends APIBaseModel { parent::__construct(); $this->methods = ["POST"]; $this->privileges = ["page-all", "page-nat-portforward-edit"]; + $this->change_note = "Deleted NAT rule via API"; } public function action() { - global $config; - $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging - $change_note = " Deleted NAT rule via API"; // Add a change note - $del_rule = $config["nat"]["rule"][$this->validated_data["id"]]; // Save the rule we are deleting - unset($config["nat"]["rule"][$this->validated_data["id"]]); // Remove rule from our config + $del_rule = $this->config["nat"]["rule"][$this->validated_data["id"]]; // Save the rule we are deleting + unset($this->config["nat"]["rule"][$this->validated_data["id"]]); // Remove rule from our config APITools\sort_nat_rules(); // Sort our NAT rules - write_config(sprintf(gettext($change_note))); // Apply our configuration change + $this->write_config(); // Apply our configuration change filter_configure(); // Ensure our firewall filter is reloaded return APIResponse\get(0, $del_rule); } public function validate_payload() { - global $config; + if (isset($this->initial_data['id'])) { // Check that our rule ID exists - if (array_key_exists($this->initial_data['id'], $config["nat"]["rule"])) { + if (array_key_exists($this->initial_data['id'], $this->config["nat"]["rule"])) { $this->validated_data["id"] = $this->initial_data['id']; } else { $this->errors[] = APIResponse\get(4016); diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRules.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRules.inc index 83925285c..70e6990f1 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRules.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRules.inc @@ -12,10 +12,10 @@ class APIFirewallRules extends APIBaseModel { } public function action() { - global $config; + // Check that we have a configuration - if (!empty($config["filter"]["rule"])) { - $rule_array = $config["filter"]["rule"]; + if (!empty($this->config["filter"]["rule"])) { + $rule_array = $this->config["filter"]["rule"]; } else { $rule_array = []; } diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesAdd.inc index 166faceed..568afb55f 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesAdd.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesAdd.inc @@ -8,23 +8,22 @@ class APIFirewallRulesAdd extends APIBaseModel { parent::__construct(); $this->methods = ["POST"]; $this->privileges = ["page-all", "page-firewall-rules-edit"]; + $this->change_note = "Added firewall rule via API"; } public function action() { - global $config; - $next_rule_id = count($config["filter"]["rule"]); // Save our next rule ID - $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging - $change_note = " Added firewall rule via API"; // Add a change note - $config["filter"]["rule"][] = $this->validated_data; // Write to our master config + + $next_rule_id = count($this->config["filter"]["rule"]); // Save our next rule ID + $this->config["filter"]["rule"][] = $this->validated_data; // Write to our master config APITools\sort_firewall_rules($this->initial_data["top"], $next_rule_id); // Sort our firewall rules - write_config(sprintf(gettext($change_note))); // Apply our configuration change + $this->write_config(); // Apply our configuration change send_event("filter reload"); // Ensure our firewall filter is reloaded return APIResponse\get(0, $this->validated_data); } // TODO: break this down into smaller field specific validators public function validate_payload() { - global $config; + $user_created_msg = $this->client->username."@".$this->client->ip_address." (API)"; $allowed_rule_types = array("pass", "block", "reject"); // Array of allowed rule types $allowed_ip_prot = array("inet", "inet6", "inet46"); // Array of allowed IP protocols diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesDelete.inc index 7ae905e7b..84c547792 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesDelete.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallRulesDelete.inc @@ -8,25 +8,23 @@ class APIFirewallRulesDelete extends APIBaseModel { parent::__construct(); $this->methods = ["POST"]; $this->privileges = ["page-all", "page-firewall-rules-edit"]; + $this->change_note = "Deleted firewall rule via API"; } public function action() { - global $config; - $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging - $change_note = " Deleted firewall rule via API"; // Add a change note - $del_rule = $config["filter"]["rule"][$this->validated_data["id"]]; // Save the rule we are deleting - unset($config["filter"]["rule"][$this->validated_data["id"]]); // Remove rule from our config + $del_rule = $this->config["filter"]["rule"][$this->validated_data["id"]]; // Save the rule we are deleting + unset($this->config["filter"]["rule"][$this->validated_data["id"]]); // Remove rule from our config APITools\sort_firewall_rules(); // Sort our firewall rules - write_config(sprintf(gettext($change_note))); // Apply our configuration change + $this->write_config(); // Apply our configuration change send_event("filter reload"); // Ensure our firewall filter is reloaded return APIResponse\get(0, $del_rule); } public function validate_payload() { - global $config; + if (isset($this->initial_data['id'])) { // Check that our rule ID exists - if (array_key_exists($this->initial_data["id"], $config["filter"]["rule"])) { + if (array_key_exists($this->initial_data["id"], $this->config["filter"]["rule"])) { $this->validated_data["id"] = $this->initial_data['id']; } else { $this->errors[] = APIResponse\get(4032); diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStates.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStates.inc new file mode 100644 index 000000000..3eb92dee8 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStates.inc @@ -0,0 +1,17 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-diagnostics-statessummary"]; + } + + public function action() { + return APIResponse\get(0, APITools\sort_state_table()); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStatesSize.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStatesSize.inc new file mode 100644 index 000000000..af13907ed --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStatesSize.inc @@ -0,0 +1,28 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-diagnostics-statessummary"]; + } + + public function action() { + + // Check our maximum state table size + if (isset($this->config["system"]["maximumstates"])) { + $size_array["maximumstates"] = intval($this->config["system"]["maximumstates"]); + } else { + $size_array["maximumstates"] = intval(pfsense_default_state_size()); + } + // Check our current state table size + $size_array["currentstates"] = count(APITools\sort_state_table()); + // Check our default state table size + $size_array["defaultmaximumstates"] = intval(pfsense_default_state_size()); + return APIResponse\get(0, $size_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStatesSizeModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStatesSizeModify.inc new file mode 100644 index 000000000..c9526b1f5 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallStatesSizeModify.inc @@ -0,0 +1,44 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-advanced-firewall"]; + $this->change_note = "Modified firewall state table size via API"; + } + + public function action() { + + // Write our state table size + if ($this->validated_data["maximumstates"] === "default") { + unset($this->config["system"]["maximumstates"]); + } else { + $this->config["system"]["maximumstates"] = $this->validated_data["maximumstates"]; + } + $this->write_config(); // Apply our configuration change + filter_configure(); // Update our firewall filter + return APIResponse\get(0, (new APIFirewallStatesSize())->action()["data"]); + } + + public function validate_payload() { + // Check if we set a maximumstates size + if (isset($this->initial_data["maximumstates"])) { + // Check that our state size is valid + if (is_numeric($this->initial_data["maximumstates"]) and (10 <= intval($this->initial_data["maximumstates"]))) { + $this->validated_data["maximumstates"] = $this->initial_data["maximumstates"]; + } elseif ($this->initial_data["maximumstates"] === "default") { + $this->validated_data["maximumstates"] = $this->initial_data["maximumstates"]; + } else { + $this->errors[] = APIResponse\get(4073); + } + } else { + $this->errors[] = APIResponse\get(4072); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPs.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPs.inc index 76f45f804..ff9ceeb9b 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPs.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPs.inc @@ -12,10 +12,10 @@ class APIFirewallVirtualIPs extends APIBaseModel { } public function action() { - global $config; + // Check that we have a virtual IP configuration - if (!empty($config["virtualip"]["vip"])) { - $vip_array = $config["virtualip"]["vip"]; + if (!empty($this->config["virtualip"]["vip"])) { + $vip_array = $this->config["virtualip"]["vip"]; } else { $vip_array = []; } diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc index f5407fca1..d31df444d 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc @@ -8,20 +8,18 @@ class APIFirewallVirtualIPsAdd extends APIBaseModel { parent::__construct(); $this->methods = ["POST"]; $this->privileges = ["page-all", "page-firewall-virtualipaddress-edit"]; + $this->change_note = "Added virtual IP via API"; } public function action() { - global $config; - $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging - $change_note = " Added virtual IP via API"; // Add a change note - $config["virtualip"]["vip"][] = $this->validated_data; // Write to our master config - write_config(sprintf(gettext($change_note))); // Apply our configuration change + $this->config["virtualip"]["vip"][] = $this->validated_data; // Write to our master config + $this->write_config(); // Apply our configuration change APITools\apply_virtual_ip($this->validated_data); // Apply our backend changes with our new configuration return APIResponse\get(0, $this->validated_data); } public function validate_payload() { - global $config; + $allowed_vip_modes = array("ipalias", "carp", "proxyarp", "other"); // Save our allowed vip modes in arra if (isset($this->initial_data['mode'])) { $mode = strtolower($this->initial_data['mode']); @@ -117,8 +115,8 @@ class APIFirewallVirtualIPsAdd extends APIBaseModel { } } // Initialize our virtual IP configuration array - if (!is_array($config["virtualip"]["vip"])) { - $config["virtualip"] = array("vip" => []); + if (!is_array($this->config["virtualip"]["vip"])) { + $this->config["virtualip"] = array("vip" => []); } // Populate our new virtual IP entry $this->validated_data["mode"] = $mode; diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc index 9f76d4358..3717f8c76 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc @@ -8,24 +8,22 @@ class APIFirewallVirtualIPsDelete extends APIBaseModel { parent::__construct(); $this->methods = ["POST"]; $this->privileges = ["page-all", "page-firewall-virtualipaddress-edit"]; + $this->change_note = "Deleted virtual IP via API"; } public function action() { - global $config; - $_SESSION["Username"] = $this->client->username; // Save our CLIENT ID to session data for logging - $change_note = " Deleted virtual IP via API"; // Add a change note - $del_vip = $config["virtualip"]["vip"][$this->validated_data["id"]]; // Save the virtual IP we are deleting + $del_vip = $this->config["virtualip"]["vip"][$this->validated_data["id"]]; // Save the virtual IP we are deleting APITools\bring_down_virtual_ip($del_vip, $this->validated_data["id"]); // Bring down v IP and delete it - write_config(sprintf(gettext($change_note))); // Apply our configuration change + $this->write_config(); // Apply our configuration change send_event("filter reload"); // Ensure our firewall filter is reloaded return APIResponse\get(0, $del_vip); } public function validate_payload() { - global $config; + if (isset($this->initial_data['id'])) { // Check that our rule ID exists - if (array_key_exists($this->initial_data["id"], $config["virtualip"]["vip"])) { + if (array_key_exists($this->initial_data["id"], $this->config["virtualip"]["vip"])) { $this->validated_data["id"] = $this->initial_data['id']; } else { $this->errors[] = APIResponse\get(4018); diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarp.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarp.inc index 67501bbfd..48b779391 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarp.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIStatusCarp.inc @@ -13,10 +13,10 @@ class APIStatusCarp extends APIBaseModel { } public function action() { - global $config; + ; $carp_status = []; $carp_status["enable"] = APITools\is_carp_enabled(); - $carp_status["maintenance_mode"] = isset($config["virtualip_carp_maintenancemode"]); + $carp_status["maintenance_mode"] = isset($this->config["virtualip_carp_maintenancemode"]); $carp_status["interfaces"] = APITools\get_carp_if_status(); return APIResponse\get(0, $carp_status); } diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemAPI.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemAPI.inc new file mode 100644 index 000000000..57de55d4f --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemAPI.inc @@ -0,0 +1,16 @@ +methods = ["GET"]; + } + + public function action() { + return APIResponse\get(0, APITools\get_api_config()[1]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemARP.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemARP.inc new file mode 100644 index 000000000..096494e18 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemARP.inc @@ -0,0 +1,17 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-diagnostics-arptable"]; + } + + public function action() { + return APIResponse\get(0, APITools\get_arp_table()); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemARPDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemARPDelete.inc new file mode 100644 index 000000000..8244169fa --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemARPDelete.inc @@ -0,0 +1,28 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-diagnostics-arptable"]; + } + + public function action() { + $del_ent = APITools\get_arp_entry("ip", $this->validated_data["ip"]); // Save our deleted ARP config + exec("arp -d ".$this->validated_data["ip"], $arp_del); // Delete our ARP address + return APIResponse\get(0, $del_ent); + } + + public function validate_payload() { + if (isset($this->initial_data['ip'])) { + $this->validated_data["ip"] = $this->initial_data['ip']; + $this->validated_data["ip"] = trim($this->validated_data["ip"]); + } else { + $this->errors[] = APIResponse\get(1006); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificates.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificates.inc new file mode 100644 index 000000000..03f9f18aa --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificates.inc @@ -0,0 +1,24 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system-certmanager"]; + } + + public function action() { + $this->validate_payload(); + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + if (!empty($this->config["cert"])) { + $this->validated_data["cert"] = $this->config["cert"]; + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificatesAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificatesAdd.inc new file mode 100644 index 000000000..f5809b2ce --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificatesAdd.inc @@ -0,0 +1,55 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-certmanager"]; + $this->change_note = "Added certificate via API"; + } + + public function action() { + $this->config["cert"][] = $this->validated_data; // Add our configuration + $this->write_config(); // Apply our configuration change + // Restart our webgui if user set this cert as active + if ($this->initial_data["active"] === true) { + APITools\restart_webconfigurator(); + } + // Print our JSON response + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + if (isset($this->initial_data['descr'])) { + $descr = $this->initial_data['descr']; + } else { + $this->errors[] = APIResponse\get(1002); + } + if (isset($this->initial_data['method'])) { + // Check what method was picked + if ($this->initial_data['method'] === "import") { + $cert = base64_decode($this->initial_data["cert"]); + $key = base64_decode($this->initial_data["key"]); + // Check if our certificate and key are valid + if (!strstr($cert, "BEGIN CERTIFICATE") || !strstr($cert, "END CERTIFICATE")) { + $this->errors[] = APIResponse\get(1003); + } elseif (cert_get_publickey($cert, false) != cert_get_publickey($key, false, 'prv')) { + $this->errors[] = APIResponse\get(1004); + } + // Populate our configuration array + $this->validated_data["refid"] = uniqid(); + $this->validated_data["type"] = "server"; + $this->validated_data["descr"] = $descr; + cert_import($this->validated_data, $cert, $key); + // If user requests this cert to be active, configure it as the wc cert + if ($this->initial_data["active"] === true) { + $this->config["system"]["webgui"]["ssl-certref"] = $this->validated_data["refid"]; + } + } + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificatesDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificatesDelete.inc new file mode 100644 index 000000000..d60030972 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemCertificatesDelete.inc @@ -0,0 +1,55 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-certmanager"]; + $this->change_note = "Deleted certificate via API"; + } + + public function action() { + $cert_del = $this->config["cert"][$this->validated_data["id"]]; // Save our cert we are deleting + if (!empty($cert_del)) { + // Check if our certificate is currently in use + if ($this->config["system"]["webgui"]["ssl-certref"] !== $cert_del["refid"]) { + unset($this->config["cert"][$this->validated_data["id"]]); // Delete our cert + $this->write_config(); // Apply our configuration change + } else { + return APIResponse\get(1005); + } + } else { + return APIResponse\get(1009); + } + + return APIResponse\get(0, $cert_del); + } + + public function validate_payload() { + if (isset($this->initial_data['refid'])) { + $refid = $this->initial_data['refid']; + // Loop through our certificates and find a match + foreach ($this->config["cert"] as $i => $c) { + if ($c["refid"] === $refid) { + $this->validated_data["id"] = $i; + break; + } + } + } elseif (isset($this->initial_data['descr'])) { + $descr = $this->initial_data['descr']; + // Loop through our certificates and find a match + foreach ($this->config["cert"] as $i => $c) { + if ($c["descr"] === $descr) { + $this->validated_data["id"] = $i; + break; + } + } + } elseif (isset($this->initial_data['id'])) { + $this->validated_data["id"] = $this->initial_data['id']; + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemConfig.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemConfig.inc new file mode 100644 index 000000000..54010637b --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemConfig.inc @@ -0,0 +1,18 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-diagnostics-backup-restore", "page-diagnostics-command"]; + } + + public function action() { + + return APIResponse\get(0, $this->config); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNS.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNS.inc new file mode 100644 index 000000000..8a7cea4cf --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNS.inc @@ -0,0 +1,34 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system"]; + } + + public function action() { + $this->validate_payload(); + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + + $this->validated_data["dnsserver"] = []; + $this->validated_data["dnsallowoverride"] = false; + $this->validated_data["dnslocalhost"] = false; + if (!empty($this->config["system"]["dnsserver"])) { + $this->validated_data["dnsserver"] = $this->config["system"]["dnsserver"]; + } + if (array_key_exists("dnsallowoverride", $this->config["system"])) { + $this->validated_data["dnsallowoverride"] = true; + } + if (array_key_exists("dnslocalhost", $this->config["system"])) { + $this->validated_data["dnslocalhost"] = true; + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNSDeleteServers.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNSDeleteServers.inc new file mode 100644 index 000000000..a5399eb61 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNSDeleteServers.inc @@ -0,0 +1,50 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system"]; + $this->change_note = "Deleted system DNS servers via API"; + } + + public function action() { + $this->write_config(); // Apply our configuration change + // Update a slew of backend services + system_resolvconf_generate(); + if (isset($this->config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($this->config['unbound']['enable'])) { + services_unbound_configure(); + } + send_event("service reload dns"); + filter_configure(); + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + if (isset($this->initial_data['dnsserver'])) { + $del_server = $this->initial_data['dnsserver']; + $curr_servers = $this->config["system"]["dnsserver"]; + $del_server = (!is_array($del_server)) ? array($del_server) : $del_server; + foreach ($del_server as $ds) { + // Ensure our config is array + if (!is_array($curr_servers)) { + $curr_servers = array($this->config["system"]["dnsserver"]); + } + // Loop through each server and check for matches, delete on match + foreach ($curr_servers as $id => $cs) { + if ($ds === $cs) { + $this->validated_data["dnsserver"][] = $ds; + unset($this->config["system"]["dnsserver"][$id]); + } + } + } + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNSModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNSModify.inc new file mode 100644 index 000000000..ad33ca13d --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemDNSModify.inc @@ -0,0 +1,60 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system"]; + $this->change_note = "Modified system DNS servers via API"; + } + + public function action() { + $this->write_config(); // Apply our configuration change + // Update a slew of backend services + system_resolvconf_generate(); + if (isset($this->config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($this->config['unbound']['enable'])) { + services_unbound_configure(); + } + + // Reload DNS services and firewall filter + send_event("service reload dns"); + filter_configure(); + return APIResponse\get(0, (new APISystemDNS())->action()["data"]); + } + + public function validate_payload() { + if (isset($this->initial_data['dnsserver'])) { + $this->initial_data["dnsserver"] = $this->initial_data['dnsserver']; + // If value is not an array, convert it + if (!is_array($this->initial_data["dnsserver"])) { + $this->initial_data["dnsserver"] = array($this->initial_data["dnsserver"]); + } + // Loop through our DNS servers and check that entry is valid + foreach ($this->initial_data["dnsserver"] as $ds) { + // Check if our DNS server is valid + if (!is_ipaddrv4($ds) and !is_ipaddrv6($ds)) { + $this->errors[] = APIResponse\get(1007); + } + } + // Add our system DNS values to validated data + $this->config["system"]["dnsserver"] = $this->initial_data["dnsserver"]; + } + if ($this->initial_data['dnsallowoverride'] === true) { + $this->config["system"]["dnsallowoverride"] = ""; + } elseif ($this->initial_data['dnsallowoverride'] === false) { + unset($this->config["system"]["dnsallowoverride"]); + } + if ($this->initial_data['dnslocalhost'] === true) { + $this->config["system"]["dnslocalhost"] = ""; + } elseif ($this->initial_data['dnslocalhost'] === false) { + unset($this->config["system"]["dnslocalhost"]); + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemHostname.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemHostname.inc new file mode 100644 index 000000000..0fa993fdf --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemHostname.inc @@ -0,0 +1,29 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system"]; + } + + public function action() { + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + + $this->validated_data = ["hostname" => "", "domain" => ""]; + if (!empty($this->config["system"]["hostname"])) { + $this->validated_data["hostname"] = $this->config["system"]["hostname"]; + } + // Check that we have a domain configuration + if (!empty($this->config["system"]["domain"])) { + $this->validated_data["domain"] = $this->config["system"]["domain"]; + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemHostnameModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemHostnameModify.inc new file mode 100644 index 000000000..de3f7df4c --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemHostnameModify.inc @@ -0,0 +1,55 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system"]; + $this->change_note = "Modified system hostname via API"; + } + + public function action() { + // Write our new hostname + $this->write_config(); // Apply our configuration change + // Update a slew of backend services + if (isset($hostname) or isset($domain)) { + system_hostname_configure(); + system_hosts_generate(); + system_resolvconf_generate(); + if (isset($this->config['dnsmasq']['enable'])) { + services_dnsmasq_configure(); + } elseif (isset($this->config['unbound']['enable'])) { + services_unbound_configure(); + } + filter_configure(); + } + $data = ["hostname" => $this->config["system"]["hostname"], "domain" => $this->config["system"]["domain"]]; + return APIResponse\get(0, $data); + } + + public function validate_payload() { + if (isset($this->initial_data['hostname'])) { + $hostname = $this->initial_data['hostname']; + $hostname = trim($hostname); + // Check if our hostname is valid + if (!is_hostname($hostname) or !is_unqualified_hostname($hostname)) { + $this->errors[] = APIResponse\get(1000); + } else { + $this->config["system"]["hostname"] = $hostname; + } + } + if (isset($this->initial_data['domain'])) { + $domain = $this->initial_data['domain']; + $domain = trim($domain); + // Check if our hostname is valid + if (!is_domain($domain)) { + $this->errors[] = APIResponse\get(1001); + } else { + $this->config["system"]["domain"] = $domain; + } + } + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemVersion.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemVersion.inc new file mode 100644 index 000000000..5a9e7a425 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemVersion.inc @@ -0,0 +1,17 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-dashboard-widgets", "page-diagnostics-command"]; + } + + public function action() { + return APIResponse\get(0, APITools\get_pfsense_version()); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsers.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsers.inc new file mode 100644 index 000000000..16b8596f0 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsers.inc @@ -0,0 +1,16 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system-usermanager"]; + } + + public function action() { + return APIResponse\get(0, $this->config["system"]["user"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDelete.inc new file mode 100644 index 000000000..3959a0321 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDelete.inc @@ -0,0 +1,37 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-usermanager"]; + $this->change_note = "Deleted user via API"; + } + + 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); + } + + public function validate_payload() { + if (isset($this->initial_data["username"])) { + if (!array_key_exists($this->initial_data["username"], index_users())) { + $this->errors[] = APIResponse\get(5001); + } else { + $this->validated_data["username"] = $this->initial_data['username']; + $this->validated_data["username"] = trim($this->validated_data["username"]); + } + } else { + $this->errors[] = APIResponse\get(5000); + } + + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDeletePrivs.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDeletePrivs.inc new file mode 100644 index 000000000..2ffa5b190 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDeletePrivs.inc @@ -0,0 +1,59 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-usermanager-addprivs"]; + $this->change_note = "Deleted privileges for user via API"; + } + + public function action() { + $user_config =& getUserEntry($this->validated_data["username"]); + $user_id = index_users()[$this->validated_data["username"]]; // Save our user's array index ID + local_user_set($user_config); // Set user backend parameters + $this->config["system"]["user"][$user_id] = $user_config; // Add our new config + $this->write_config(); // Write to config + return APIResponse\get(0, $this->validated_data["priv"]); + } + + public function validate_payload() { + global $priv_list; + if (isset($this->initial_data['username'])) { + $this->validated_data["username"] = $this->initial_data['username']; + $this->validated_data["username"] = trim($this->validated_data["username"]); + } else { + $this->errors[] = APIResponse\get(5000); + } + if (isset($this->initial_data['priv'])) { + $this->validated_data["priv"] = $this->initial_data['priv']; + } else { + $this->errors[] = APIResponse\get(5004); + } + // Check if our user already exists, if so exit on non-zero + $user_config =& getUserEntry($this->validated_data["username"]); + if (!array_key_exists("uid", $user_config)) { + $this->errors[] = APIResponse\get(5002); + } + // Ensure our new priv is array, if it is a string create an array containing the string + if (is_string($this->validated_data["priv"])) { + $this->validated_data["priv"] = array($this->validated_data["priv"]); + } + if (is_array($this->validated_data["priv"])) { + // Loop through our new priv list and check that the privs are valid + foreach ($this->validated_data["priv"] as $dp) { + if (!array_key_exists($dp, $priv_list)) { + $this->errors[] = APIResponse\get(5006); + } + if (in_array($dp, $user_config["priv"])) { + $user_config["priv"] = \array_diff($user_config["priv"], array($dp)); + } + } + } else { + $this->errors[] = APIResponse\get(5005); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc index 6d70eb3ac..836dd17a2 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIBaseModel.inc @@ -8,17 +8,22 @@ class APIBaseModel { public $privileges; public $initial_data; public $validated_data; + public $config; public $errors; public $methods; + public $change_note; public $requires_auth; public $set_auth_mode; public function __construct() { + global $config; + $this->config =& $config; $this->methods = ["GET", "POST"]; $this->privileges = ["page-all"]; $this->client = null; $this->requires_auth = true; $this->set_auth_mode = null; + $this->change_note = "Made unknown change via API"; $this->initial_data = APITools\get_request_data(); $this->validated_data = []; $this->errors = []; @@ -32,6 +37,12 @@ class APIBaseModel { return APIResponse\get(10); } + protected function write_config() { + $_SESSION["Username"] = $this->client->username; // Save our user to session data for logging + write_config(sprintf(gettext(" ".$this->change_note))); // Apply our configuration change + unset($_SESSION); + } + public function validate_payload() { # This function is intended to be overridden by an API model extended class # Any payload validation must be specified here diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc index 9715c894c..b961dfa7f 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc @@ -71,7 +71,68 @@ function get($id, $data=[]) { "return" => $id, "message" => "Your API request was valid but no actions were specified for this endpoint", ], - + // 1000-1999 reserved for /system API calls + 1000 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid system hostname" + ], + 1001 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid system hostname domain" + ], + 1002 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "System certificate descriptive name required" + ], + 1003 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid system certificate" + ], + 1004 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid system certificate key" + ], + 1005 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "System certificate in use" + ], + 1006 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "ARP IP required" + ], + 1007 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid system DNS server IP address" + ], + 1008 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "System DNS server IP address required" + ], + 1009 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Could not locate system certificate" + ], + 4000 => [ "status" => "bad request", "code" => 400, @@ -516,6 +577,157 @@ function get($id, $data=[]) { "return" => $id, "message" => "Invalid firewall state table size" ], + //5000-5999 reserved for /users API calls + 5000 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Username required" + ], + 5001 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "User does not exist" + ], + 5002 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "User already exists" + ], + 5003 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "User password required" + ], + 5004 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "User privilege required" + ], + 5005 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "User privilege must be type array or string" + ], + 5006 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown user privilege" + ], + 5007 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "User group name required" + ], + 5008 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown group name" + ], + 5009 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Group must be type array or string" + ], + 5010 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "User authentication server name required" + ], + 5011 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "LDAP authentication server hostname or IP required" + ], + 5012 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid LDAP authentication server hostname or IP" + ], + 5013 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server LDAP port required" + ], + 5014 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid authentication server LDAP port" + ], + 5015 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server LDAP URL type required" + ], + 5016 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid authentication server LDAP URL type" + ], + 5017 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid authentication server LDAP protocol version" + ], + 5018 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server LDAP search scope required" + ], + 5019 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid authentication server LDAP search scope" + ], + 5020 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server LDAP bind DN required" + ], + 5021 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server LDAP bind password required" + ], + 5022 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server LDAP user naming attribute required" + ], + 5023 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server LDAP group naming attribute required" + ], + 5024 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server LDAP group member attribute required" + ], ]; $response = $responses[$id]; diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index 40a7adf34..c90b7a7ba 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -8,6 +8,7 @@ require_once("config.inc"); require_once("util.inc"); require_once("interfaces.inc"); require_once("interfaces_fast.inc"); +require_once("priv.defs.inc"); require_once("service-utils.inc"); require_once("filter.inc"); require_once("shaper.inc"); @@ -582,4 +583,68 @@ function sanitize_str($string) { $string = str_replace(' ', '_', $string); // Replace whitespace with underscore $string = preg_replace('/[^A-Za-z0-9\-_.]/', '', $string); // Remove special characters return $string; +} + +// Sort our state table +function sort_state_table() { + // Local variables + $states = shell_exec("/sbin/pfctl -s state"); + $state_table = array(); + // Parse our output + $state_rows = explode("\n", $states); + foreach ($state_rows as $id => $data) { + $data = preg_replace('!\s+!', ' ', $data); // Remove extra whitespace + $data_fields = explode(" ", $data); // Split on whitespace + if (count($data_fields) > 1) { + $state_table[$id] = array(); + $state_table[$id]["interface"] = $data_fields[0]; + $state_table[$id]["protocol"] = $data_fields[1]; + $state_table[$id]["source"] = ($data_fields[3] === "->") ? $data_fields[2] : $data_fields[4]; + $state_table[$id]["destination"] = ($data_fields[3] === "->") ? $data_fields[4] : $data_fields[2]; + $state_table[$id]["status"] = $data_fields[5]; + } + } + return $state_table; +} + +// Restarts the pfSense webConfigurator +function restart_webconfigurator() { + ob_flush(); + flush(); + log_error(gettext("webConfigurator configuration has changed. Restarting webConfigurator.")); + send_event("service restart webgui"); +} + +// Parse our ARP table into an array +function get_arp_table() { + // Local variables + $arp_cmd = "arp -an"; // Assign the command which reads our ARP table + exec($arp_cmd, $arp_data); // Output our ARP table into a string + $arp_table = array(); // Init our ARP table array + // Loop through each line of our ARP data and parse into our array + foreach ($arp_data as $arp_line) { + $elements = explode(' ', $arp_line, 7); + $arp_entry = array(); + $arp_entry['ip'] = trim(str_replace(array('(', ')'), '', $elements[1])); + $arp_entry['mac'] = trim($elements[3]); + $arp_entry['interface'] = trim($elements[5]); + $arp_entry['status'] = trim(substr($elements[6], 0, strrpos($elements[6], ' '))); + $arp_entry['linktype'] = trim(str_replace(array('[', ']'), '', strrchr($elements[6], ' '))); + $arp_table[] = $arp_entry; + } + return $arp_table; +} + +// Pull a single ARP entry value from our ARP table +function get_arp_entry($search, $value) { + // Local variables + $arp_table = get_arp_table(); // Pull our ARP table + $arp_match = []; // Init our match array + // Loop through ARP table and look for matches + foreach ($arp_table as $arp_ent) { + if ($arp_ent[$search] === $value) { + $arp_match = $arp_ent; + } + } + return $arp_match; } \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/add/address/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/add/address/index.php index b69c28c3c..f46487063 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/add/address/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/add/address/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/add/index.php index 62fd09352..7a091e90b 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/add/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/delete/address/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/delete/address/index.php index 332850bed..303e26128 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/delete/address/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/delete/address/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/modify/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/modify/index.php index a4eaa85cc..caa478e65 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/modify/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/aliases/modify/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/index.php index 607101348..4d7f0ca25 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/size/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/size/index.php index ca806819e..669661007 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/size/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/size/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/size/modify/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/size/modify/index.php index f6a63fb8f..0465a21d7 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/size/modify/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/firewall/states/size/modify/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/api/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/api/index.php index 7fa44d1c3..8f611ab36 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/api/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/api/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/arp/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/arp/delete/index.php index b9ce388c1..7e04578da 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/arp/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/arp/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/arp/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/arp/index.php index 70a8ddedc..8e7237bae 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/arp/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/arp/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/add/index.php index da44db4c1..1258ff700 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/add/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/delete/index.php index b0459c827..da7248f1b 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/index.php index 6b8100c72..74c3e3d2e 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/certificates/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/config/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/config/index.php index 3edf26eb9..93cdafd34 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/config/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/config/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/delete/servers/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/delete/servers/index.php index 9735af27b..b2026c41e 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/delete/servers/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/delete/servers/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/index.php index 29a66a613..08fee7bd0 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/modify/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/modify/index.php index 31f5990c5..1471e2ea5 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/modify/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/dns/modify/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/hostname/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/hostname/index.php index f59a14c66..02f4feed5 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/hostname/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/hostname/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/hostname/modify/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/hostname/modify/index.php index bdbe70bf8..9efb18cce 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/hostname/modify/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/hostname/modify/index.php @@ -1,10 +1,4 @@ listen(); diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/version/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/version/index.php index fcc3bf54d..504fc373d 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/version/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/version/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/index.php index df86dc2a7..5537cde05 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/privs/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/privs/index.php index 8a465e281..034023628 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/privs/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/privs/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/index.php index 6b71e001f..2801d6988 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/index.php @@ -1,10 +1,4 @@ listen(); From 33664c28436a59a0d2ad58a36752a635d6700a9c Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Mon, 24 Aug 2020 20:32:56 -0600 Subject: [PATCH 11/16] Converted system API endpoints to OOP, converting services to OOP --- .../inc/api/api_models/APIRoutingGateways.inc | 20 +++ .../etc/inc/api/api_models/APIServices.inc | 39 ++++ .../api/api_models/APIServicesNtpdRestart.inc | 17 ++ .../api/api_models/APIServicesNtpdStart.inc | 17 ++ .../api/api_models/APIServicesNtpdStop.inc | 17 ++ .../inc/api/api_models/APIServicesRestart.inc | 22 +++ .../inc/api/api_models/APIServicesSSHd.inc | 20 +++ .../api/api_models/APIServicesSSHdModify.inc | 74 ++++++++ .../api/api_models/APIServicesSSHdRestart.inc | 17 ++ .../api/api_models/APIServicesSSHdStart.inc | 17 ++ .../api/api_models/APIServicesSSHdStop.inc | 17 ++ .../inc/api/api_models/APIServicesStart.inc | 22 +++ .../inc/api/api_models/APIServicesStop.inc | 22 +++ .../api_models/APIServicesSyslogdRestart.inc | 17 ++ .../api_models/APIServicesSyslogdStart.inc | 17 ++ .../api/api_models/APIServicesSyslogdStop.inc | 17 ++ .../etc/inc/api/api_models/APIUsersAdd.inc | 61 +++++++ .../inc/api/api_models/APIUsersAddGroups.inc | 55 ++++++ .../inc/api/api_models/APIUsersAddPrivs.inc | 53 ++++++ .../api/api_models/APIUsersAuthservers.inc | 21 +++ .../api_models/APIUsersAuthserversDelete.inc | 45 +++++ .../api_models/APIUsersAuthserversLDAP.inc | 24 +++ .../api_models/APIUsersAuthserversLDAPAdd.inc | 168 ++++++++++++++++++ .../APIUsersAuthserversLDAPDelete.inc | 45 +++++ .../api_models/APIUsersAuthserversRADIUS.inc | 24 +++ .../APIUsersAuthserversRADIUSDelete.inc | 45 +++++ .../api/api_models/APIUsersDeleteGroups.inc | 62 +++++++ .../etc/inc/api/api_models/APIUsersModify.inc | 63 +++++++ .../etc/inc/api/framework/APIResponse.inc | 97 ++++++++++ .../files/etc/inc/api/framework/APITools.inc | 12 ++ pfSense-pkg-API/files/etc/inc/apicalls.inc | 7 +- .../www/api/v1/routing/gateways/index.php | 10 +- .../usr/local/www/api/v1/services/index.php | 10 +- .../api/v1/services/ntpd/restart/index.php | 10 +- .../www/api/v1/services/ntpd/start/index.php | 10 +- .../www/api/v1/services/ntpd/stop/index.php | 10 +- .../www/api/v1/services/restart/index.php | 10 +- .../local/www/api/v1/services/sshd/index.php | 10 +- .../www/api/v1/services/sshd/modify/index.php | 10 +- .../api/v1/services/sshd/restart/index.php | 10 +- .../www/api/v1/services/sshd/start/index.php | 10 +- .../www/api/v1/services/sshd/stop/index.php | 10 +- .../local/www/api/v1/services/start/index.php | 10 +- .../local/www/api/v1/services/stop/index.php | 10 +- .../api/v1/services/syslogd/restart/index.php | 10 +- .../api/v1/services/syslogd/start/index.php | 10 +- .../api/v1/services/syslogd/stop/index.php | 10 +- .../www/api/v1/users/add/groups/index.php | 10 +- .../usr/local/www/api/v1/users/add/index.php | 10 +- .../www/api/v1/users/add/privs/index.php | 10 +- .../api/v1/users/authservers/delete/index.php | 10 +- .../www/api/v1/users/authservers/index.php | 10 +- .../v1/users/authservers/ldap/add/index.php | 10 +- .../users/authservers/ldap/delete/index.php | 10 +- .../api/v1/users/authservers/ldap/index.php | 10 +- .../users/authservers/radius/delete/index.php | 10 +- .../api/v1/users/authservers/radius/index.php | 10 +- .../www/api/v1/users/delete/groups/index.php | 10 +- .../local/www/api/v1/users/modify/index.php | 10 +- 59 files changed, 1207 insertions(+), 227 deletions(-) create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIRoutingGateways.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServices.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdRestart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdStart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdStop.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesRestart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdModify.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdRestart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdStart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdStop.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesStart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesStop.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdRestart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdStart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdStop.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAddGroups.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAddPrivs.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthservers.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAP.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAPAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAPDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversRADIUS.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversRADIUSDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDeleteGroups.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersModify.inc diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIRoutingGateways.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIRoutingGateways.inc new file mode 100644 index 000000000..7eb532530 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIRoutingGateways.inc @@ -0,0 +1,20 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system-gateways"]; + } + + public function action() { + $gw_array = []; + if (!empty($this->config["gateways"]["gateway_item"])) { + $gw_array = $this->config["gateways"]["gateway_item"]; + } + return APIResponse\get(0, $gw_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServices.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServices.inc new file mode 100644 index 000000000..45a114343 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServices.inc @@ -0,0 +1,39 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + $service_list = get_services(); // Stop our service + // Loop through our service list and add our service status + foreach ($service_list as $key => $srvc) { + $s_status = get_service_status($srvc); + // Check if service is started + if ($s_status === true) { + $service_list[$key]["status"] = "running"; + } elseif ($s_status === false) { + $service_list[$key]["status"] = "stopped"; + } else { + $service_list[$key]["status"] = "unknown"; + } + // Check if user requested only one service, if so remove unmatched services + if (isset($this->validated_data["name"]) and $this->validated_data["name"] !== $srvc["name"]) { + unset($service_list[$key]); + } + } + return APIResponse\get(0, $service_list); + } + + public function validate_payload() { + if (isset($this->initial_data['name'])) { + $this->validated_data["name"] = $this->initial_data['name']; + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdRestart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdRestart.inc new file mode 100644 index 000000000..5565e7c5c --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdRestart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_restart("ntpd", []); // Start our service + return APIResponse\get(0, ["ntpd" => "restarted"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdStart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdStart.inc new file mode 100644 index 000000000..9651e84b8 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdStart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_start("ntpd", []); // Start our service + return APIResponse\get(0, ["ntpd" => "started"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdStop.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdStop.inc new file mode 100644 index 000000000..1978d2a01 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesNtpdStop.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_stop("ntpd", []); + return APIResponse\get(0, ["ntpd" => "stopped"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesRestart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesRestart.inc new file mode 100644 index 000000000..43757a209 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesRestart.inc @@ -0,0 +1,22 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + $services = get_services(); + // Loop through our service list and add our service status + foreach ($services as $key => $srvc) { + service_control_restart($srvc["name"], []); // Start our service + $services[$key]["status"] = "restarted"; + } + return APIResponse\get(0, $services); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHd.inc new file mode 100644 index 000000000..820b1b274 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHd.inc @@ -0,0 +1,20 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system-advanced-admin"]; + } + + public function action() { + $ssh_array = []; + if (!empty($this->config["system"]["ssh"])) { + $ssh_array = $this->config["system"]["ssh"]; + } + return APIResponse\get(0, $ssh_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdModify.inc new file mode 100644 index 000000000..9a6ff697d --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdModify.inc @@ -0,0 +1,74 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-advanced-admin"]; + $this->change_note = "Modified sshd configuration via API"; + } + + public function action() { + $this->write_config(); + send_event("filter reload"); + // Check that something was changed before altering service + if (!empty($this->validated_data)) { + killbyname("sshd"); // Kill SSHD + log_error(gettext("secure shell configuration has changed. Stopping sshd.")); + if ($this->config['system']['ssh']['enable']) { + log_error(gettext("secure shell configuration has changed. Restarting sshd.")); + send_event("service restart sshd"); + } + } + return APIResponse\get(0, $this->config["system"]["ssh"]); + } + + public function validate_payload() { + if (isset($this->initial_data['enable'])) { + $enable = $this->initial_data['enable']; + if ($enable === true) { + $this->config["system"]["ssh"]["enable"] = "enabled"; + } elseif ($enable === false) { + unset($this->config["system"]["ssh"]["enable"]); + } else { + $this->errors[] = APIResponse\get(2000); + } + } + if (isset($this->initial_data["sshdkeyonly"])) { + $this->validated_data["sshdkeyonly"] = $this->initial_data["sshdkeyonly"]; + $allowed_auth_types = array("disabled", "enabled", "both"); // Array of allowed auth types + // Check if our auth type is valid + if (in_array($this->validated_data["sshdkeyonly"], $allowed_auth_types)) { + if ($this->validated_data["sshdkeyonly"] === "disabled") { + unset($this->config["system"]["ssh"]["sshdkeyonly"]); + } else { + $this->config["system"]["ssh"]["sshdkeyonly"] = $this->validated_data["sshdkeyonly"]; + } + } else { + $this->errors[] = APIResponse\get(2001); + } + } + if (isset($this->initial_data['sshdagentforwarding'])) { + $this->validated_data["sshdagentforwarding"] = $this->initial_data['sshdagentforwarding']; + if ($this->validated_data["sshdagentforwarding"] === true) { + $this->config["system"]["ssh"]["sshdagentforwarding"] = "enabled"; + } elseif ($this->validated_data["sshdagentforwarding"] === false) { + unset($this->config["system"]["ssh"]["sshdagentforwarding"]); + } else { + $this->errors[] = APIResponse\get(2002); + } + } + if (isset($this->initial_data['port'])) { + $this->validated_data["port"] = strval($this->initial_data['port']); + // Convert string to array + if (is_port($this->validated_data["port"])) { + $this->config["system"]["ssh"]["port"] = $this->validated_data["port"]; + } else { + $this->errors[] = APIResponse\get(2003); + } + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdRestart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdRestart.inc new file mode 100644 index 000000000..1a5e61e39 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdRestart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_restart("sshd", []); + return APIResponse\get(0, ["sshd" => "restarted"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdStart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdStart.inc new file mode 100644 index 000000000..028c56f09 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdStart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_start("sshd", []); // Start our service + return APIResponse\get(0, ["sshd" => "started"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdStop.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdStop.inc new file mode 100644 index 000000000..cd69cef2d --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSSHdStop.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_stop("sshd", []); + return APIResponse\get(0, ["sshd" => "stopped"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesStart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesStart.inc new file mode 100644 index 000000000..9e36a5a60 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesStart.inc @@ -0,0 +1,22 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + $services = get_services(); + // Loop through our service list and add our service status + foreach ($services as $key => $srvc) { + service_control_start($srvc["name"], []); // Start our service + $services[$key]["status"] = "started"; + } + return APIResponse\get(0, $services); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesStop.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesStop.inc new file mode 100644 index 000000000..c2acb805a --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesStop.inc @@ -0,0 +1,22 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + $services = get_services(); + // Loop through our service list and add our service status + foreach ($services as $key => $srvc) { + service_control_stop($srvc["name"], []); // Start our service + $services[$key]["status"] = "stopped"; + } + return APIResponse\get(0, $services); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdRestart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdRestart.inc new file mode 100644 index 000000000..4fee3c2dd --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdRestart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_restart("syslogd", []); + return APIResponse\get(0, ["syslogd" => "restarted"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdStart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdStart.inc new file mode 100644 index 000000000..b0925df8e --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdStart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_start("syslogd", []); + return APIResponse\get(0, ["syslogd" => "started"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdStop.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdStop.inc new file mode 100644 index 000000000..2ed3f8bed --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesSyslogdStop.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_stop("syslogd", []); + return APIResponse\get(0, ["syslogd" => "stopped"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAdd.inc new file mode 100644 index 000000000..b41b777d1 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAdd.inc @@ -0,0 +1,61 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-usermanager"]; + $this->change_note = "Added user via API"; + } + + public function action() { + $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); + $this->write_config(); + $userindex = index_users(); // Update our user index + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + $this->validated_data["uid"] = $this->config["system"]["nextuid"]; // Save our next UID + 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); + } else { + $this->validated_data["name"] = trim($this->initial_data['username']); + } + } else { + $this->errors[] = APIResponse\get(5000); + } + if (isset($this->initial_data['password'])) { + $this->validated_data["password"] = trim($this->initial_data['password']); + } else { + $this->errors[] = APIResponse\get(5003); + } + 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 + } + if (isset($this->initial_data['descr'])) { + $this->validated_data["descr"] = trim($this->initial_data['descr']); // Update our user's full name + } + if (isset($this->initial_data['expires'])) { + $this->validated_data["expires"] = trim($this->initial_data['expires']); // Update our user's expiration date + } + if (isset($this->initial_data['authorizedkeys'])) { + $this->validated_data["authorizedkeys"] = trim($this->initial_data['authorizedkeys']); // Update our user's authorized keys + } + if (isset($this->initial_data['ipsecpsk'])) { + $this->validated_data["ipsecpsk"] = trim($this->initial_data['ipsecpsk']); // Update our user's IPsec pre-shared key + } + $this->validated_data["scope"] = "user"; // Set our new user's system scope + $this->validated_data["priv"] = []; // Default our privs to empty array + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAddGroups.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAddGroups.inc new file mode 100644 index 000000000..0e8a6fb7b --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAddGroups.inc @@ -0,0 +1,55 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-groupmanager"]; + $this->change_note = "Added group membership for user via API"; + } + + public function action() { + local_user_set_groups($this->validated_data["user_config"], $this->validated_data["user_groups"]); // Set our user's groups + $this->write_config(); + return APIResponse\get(0, $this->validated_data["user_groups"]); + } + + public function validate_payload() { + if (isset($this->initial_data['username'])) { + if (!array_key_exists($this->initial_data['username'], index_users())) { + $this->errors[] = APIResponse\get(5001); + } else { + $this->validated_data["username"] = $this->initial_data['username']; + $this->validated_data["user_config"] = getUserEntry($this->validated_data["username"]); + $user_groups = local_user_get_groups($this->validated_data["user_config"], true); + } + } else { + $this->errors[] = APIResponse\get(5000); + } + if (isset($this->initial_data['group'])) { + if (is_string($this->initial_data['group'])) { + $this->initial_data['group'] = array($this->initial_data['group']); + } + if (is_array($this->initial_data['group'])) { + $this->validated_data["user_groups"] = []; + foreach ($this->initial_data['group'] as $ng) { + if (!array_key_exists($ng, index_groups())) { + $this->errors[] = APIResponse\get(5008); + } + if (!in_array($ng, $user_groups)) { + $this->validated_data["user_groups"][] = $ng; + } + } + // Add our new groups to our existing groups + $this->validated_data["user_groups"] = array_merge($user_groups, $this->validated_data["user_groups"]); + } else { + $this->errors[] = APIResponse\get(5009); + } + } else { + $this->errors[] = APIResponse\get(5007); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAddPrivs.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAddPrivs.inc new file mode 100644 index 000000000..0cbd8d1ef --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAddPrivs.inc @@ -0,0 +1,53 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-usermanager"]; + $this->change_note = "Added privileges for user via API"; + } + + public function action() { + local_user_set($this->validated_data["user_config"]); // Set user backend parameters + $this->write_config(); + return APIResponse\get(0, $this->validated_data["user_config"]["priv"]); + } + + public function validate_payload() { + global $priv_list; + if (isset($this->initial_data['username'])) { + $this->validated_data["username"] = trim($this->initial_data['username']); + $this->validated_data["user_config"] =& getUserEntry($this->validated_data["username"]); + if (!array_key_exists("uid", $this->validated_data["user_config"])) { + $this->errors[] = APIResponse\get(5001); + } + } else { + $this->errors[] = APIResponse\get(5000); + } + if (isset($this->initial_data['priv'])) { + // Ensure our new priv is array, if it is a string create an array containing the string + if (is_string($this->initial_data["priv"])) { + $this->initial_data["priv"] = array($this->initial_data["priv"]); + } + if (is_array($this->initial_data["priv"])) { + // Loop through our new priv list and check that the privs are valid + foreach ($this->initial_data["priv"] as $np) { + if (!array_key_exists($np, $priv_list)) { + $this->errors[] = APIResponse\get(5006); + } + if (!in_array($np, $this->validated_data["user_config"]["priv"])) { + $this->validated_data["user_config"]["priv"][] = $np; + } + } + } else { + $this->errors[] = APIResponse\get(5005); + } + } else { + $this->errors[] = APIResponse\get(5004); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthservers.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthservers.inc new file mode 100644 index 000000000..176aa9823 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthservers.inc @@ -0,0 +1,21 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system-authservers"]; + } + + public function action() { + if (!empty($this->config["system"]["authserver"])) { + $as_array = $this->config["system"]["authserver"]; + } else { + $as_array = []; + } + return APIResponse\get(0, $as_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversDelete.inc new file mode 100644 index 000000000..b4cc7e95c --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversDelete.inc @@ -0,0 +1,45 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-user-authserver"]; + $this->change_note = "Deleted authentication server via API"; + } + + public function action() { + $del_server = $this->config["system"]["authserver"][$this->validated_data["id"]]; + unset($this->config["system"]["authserver"][$this->validated_data["id"]]); + if ($this->validated_data["revert"] === true) { + unset($this->config["system"]["webgui"]["authmode"]); + } + $this->write_config(); + return APIResponse\get(0, $del_server); + } + + public function validate_payload() { + if (isset($this->initial_data['name'])) { + $this->validated_data["name"] = $this->initial_data['name']; + } else { + $this->errors[] = APIResponse\get(5010); + } + + // Loop through our servers and check that this server exists + foreach ($this->config["system"]["authserver"] as $asid => $asc) { + if ($this->validated_data["name"] === $asc["name"]) { + $this->validated_data["id"] = $asid; // Save our config before deleting + // Check if this auth server is our default, if so revert to local database + if ($this->validated_data["name"] === $this->config["system"]["webgui"]["authmode"]) { + $this->validated_data["revert"] = True; + } + } + } + if (is_null($this->validated_data["id"])) { + $this->errors[] = APIResponse\get(5025); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAP.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAP.inc new file mode 100644 index 000000000..3007c0167 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAP.inc @@ -0,0 +1,24 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system-authservers"]; + } + + public function action() { + $as_array = []; + if (!empty($this->config["system"]["authserver"])) { + foreach ($this->config["system"]["authserver"] as $a) { + if ($a["type"] === "ldap") { + $as_array[] = $a; + } + } + } + return APIResponse\get(0, $as_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAPAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAPAdd.inc new file mode 100644 index 000000000..436aae589 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAPAdd.inc @@ -0,0 +1,168 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-user-authserver"]; + $this->change_note = "Added LDAP authentication server via API"; + } + + public function action() { + $this->config["system"]["authserver"][] = $this->validated_data; // Add our new configuration + // Check if clients wants to set this as default auth server + if ($this->initial_data["active"] === true) { + $this->config['system']['webgui']['authmode'] = $this->validated_data["name"]; // Add default auth server + } + $this->write_config(); + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + $ldap_count = count($this->config["system"]["authserver"]); // Save our total number of auth servers + $this->validated_data = ["refid" => uniqid(), "type" => "ldap"]; // Init our new LDAP config + if (isset($this->initial_data['name'])) { + $this->validated_data["name"] = APITools\sanitize_str($this->initial_data['name']); + if (APITools\is_authentication_server($this->validated_data["name"])) { + $this->errors[] = APIResponse\get(5026); + } + } else { + while (true) { + $this->validated_data["name"] = "LDAP_AUTHSERVER_" . strval($ldap_count); + // Check if auto name is already used + if (APITools\is_authentication_server($this->validated_data["name"])) { + $ldap_count++; + } else { + break; + } + } + } + if (isset($this->initial_data['host'])) { + $this->validated_data["host"] = $this->initial_data['host']; + // Check if our hostname is valid + if (!is_hostname($this->validated_data["host"]) and !is_ipaddrv4($this->validated_data["host"])) { + $this->errors[] = APIResponse\get(5012); + } + } else { + $this->errors[] = APIResponse\get(5011); + } + if (($this->initial_data['ldap_port'])) { + $this->validated_data["ldap_port"] = strval($this->initial_data['ldap_port']); + // Check if our hostname is valid + if (!is_port($this->validated_data["ldap_port"])) { + $this->errors[] = APIResponse\get(5014); + } + } else { + $this->errors[] = APIResponse\get(5013); + + } + if (isset($this->initial_data['ldap_urltype'])) { + // Assign default config values + $allowed_url_types = array( + "standard" => "TCP - Standard", + "starttls" => "TCP - STARTTLS", + "encrypted" => "SSL - Encrypted" + ); + // Accommodate config value changes for pfSense 2.5_ + if (APITools\get_pfsense_version()["program"] >= 250) { + $allowed_url_types = array( + "standard" => "Standard TCP", + "starttls" => "STARTTLS Encrypted", + "encrypted" => "SSL/TLS Encrypted" + ); + } + // Check if our URL type is allowed + if (!array_key_exists($this->initial_data["ldap_urltype"], $allowed_url_types)) { + $this->errors[] = APIResponse\get(5016); + } + $this->validated_data["ldap_urltype"] = $allowed_url_types[$this->initial_data['ldap_urltype']]; + } else { + $this->errors[] = APIResponse\get(5015); + } + if (isset($this->initial_data['ldap_protver'])) { + $this->validated_data["ldap_protver"] = intval($this->initial_data['ldap_protver']); + // Check if our LDAP version is valid + $allowed_ldap_ver = array(2, 3); // Array of allowed LDAP protocol versions + if (!in_array($this->validated_data["ldap_protver"], $allowed_ldap_ver)) { + $this->errors[] = APIResponse\get(5017); + } + } else { + $this->validated_data["ldap_protver"] = 3; + } + if (isset($this->initial_data['ldap_timeout'])) { + $this->validated_data["ldap_timeout"] = intval($this->initial_data['ldap_timeout']); + } else { + $this->validated_data["ldap_timeout"] = 25; // Assign default if not specified + } + if (isset($this->initial_data['ldap_scope'])) { + $this->validated_data["ldap_scope"] = $this->initial_data['ldap_scope']; + // Check if our LDAP scope is valid + $allowed_ldap_scopes = array("one", "subtree"); // Array of allowed LDAP scopes + if (!in_array($this->validated_data["ldap_scope"], $allowed_ldap_scopes)) { + $this->errors[] = APIResponse\get(5019); + } + } else { + $this->errors[] = APIResponse\get(5018); + } + if (isset($this->initial_data['ldap_basedn'])) { + $this->validated_data["ldap_basedn"] = strval($this->initial_data['ldap_basedn']); + } else { + $this->validated_data["ldap_basedn"] = ""; + } + if (isset($this->initial_data['ldap_authcn'])) { + $this->validated_data["ldap_authcn"] = strval($this->initial_data['ldap_authcn']); + } else { + $this->validated_data["ldap_authcn"] = ""; + } + if (isset($this->initial_data['ldap_extended_query'])) { + $this->validated_data["ldap_extended_enabled"] = "yes"; + $this->validated_data["ldap_extended_query"] = strval($this->initial_data['ldap_extended_query']); + } + if (!isset($this->initial_data['ldap_binddn']) and !isset($this->initial_data['ldap_bindpw'])) { + $bind_anon = true; + } + if (!$bind_anon) { + if (isset($this->initial_data['ldap_binddn'])) { + $this->validated_data["ldap_binddn"] = strval($this->initial_data['ldap_binddn']); + } else { + $this->errors[] = APIResponse\get(5020); + } + if (isset($this->initial_data['ldap_bindpw'])) { + $this->validated_data["ldap_bindpw"] = strval($this->initial_data['ldap_bindpw']); + } else { + $this->errors[] = APIResponse\get(5021); + } + } + if (isset($this->initial_data['ldap_attr_user'])) { + $this->validated_data["ldap_attr_user"] = strval($this->initial_data['ldap_attr_user']); + } else { + $this->errors[] = APIResponse\get(5022); + } + if (isset($this->initial_data['ldap_attr_group'])) { + $this->validated_data["ldap_attr_group"] = strval($this->initial_data['ldap_attr_group']); + } else { + $this->errors[] = APIResponse\get(5023); + } + if (isset($this->initial_data['ldap_attr_member'])) { + $this->validated_data["ldap_attr_member"] = strval($this->initial_data['ldap_attr_member']); + } else { + $this->errors[] = APIResponse\get(5024); + } + if (isset($this->initial_data['ldap_attr_groupobj'])) { + $this->validated_data["ldap_rfc2307"] = ""; // Enable RFC2307 mode + $this->validated_data["ldap_attr_groupobj"] = strval($this->initial_data['ldap_attr_groupobj']); + } + if ($this->initial_data['ldap_utf8'] === true) { + $this->validated_data["ldap_utf8"] = ""; // Enable UTF8 LDAP parameters + } + if ($this->initial_data['ldap_nostrip_at'] === true) { + $this->validated_data["ldap_nostrip_at"] = ""; // Disable LDAP username alterations (@) + } + if ($this->initial_data['active'] === true) { + $this->initial_data["active"] = true; + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAPDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAPDelete.inc new file mode 100644 index 000000000..98e0b59f6 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversLDAPDelete.inc @@ -0,0 +1,45 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-user-authserver"]; + $this->change_note = "Deleted LDAP authentication server via API"; + } + + public function action() { + $del_server = $this->config["system"]["authserver"][$this->validated_data["id"]]; + unset($this->config["system"]["authserver"][$this->validated_data["id"]]); + if ($this->validated_data["revert"] === true) { + unset($this->config["system"]["webgui"]["authmode"]); + } + $this->write_config(); + return APIResponse\get(0, $del_server); + } + + public function validate_payload() { + if (isset($this->initial_data['name'])) { + $this->validated_data["name"] = $this->initial_data['name']; + } else { + $this->errors[] = APIResponse\get(5010); + } + + // Loop through our servers and check that this server exists + foreach ($this->config["system"]["authserver"] as $asid => $asc) { + if ($this->validated_data["name"] === $asc["name"] and $asc["type"] === "ldap") { + $this->validated_data["id"] = $asid; // Save our config before deleting + // Check if this auth server is our default, if so revert to local database + if ($this->validated_data["name"] === $this->config["system"]["webgui"]["authmode"]) { + $this->validated_data["revert"] = True; + } + } + } + if (is_null($this->validated_data["id"])) { + $this->errors[] = APIResponse\get(5025); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversRADIUS.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversRADIUS.inc new file mode 100644 index 000000000..ae4f5637d --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversRADIUS.inc @@ -0,0 +1,24 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-system-authservers"]; + } + + public function action() { + $as_array = []; + if (!empty($this->config["system"]["authserver"])) { + foreach ($this->config["system"]["authserver"] as $a) { + if ($a["type"] === "radius") { + $as_array[] = $a; + } + } + } + return APIResponse\get(0, $as_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversRADIUSDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversRADIUSDelete.inc new file mode 100644 index 000000000..cb79d8ad0 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersAuthserversRADIUSDelete.inc @@ -0,0 +1,45 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-user-authserver"]; + $this->change_note = "Deleted RADIUS authentication server via API"; + } + + public function action() { + $del_server = $this->config["system"]["authserver"][$this->validated_data["id"]]; + unset($this->config["system"]["authserver"][$this->validated_data["id"]]); + if ($this->validated_data["revert"] === true) { + unset($this->config["system"]["webgui"]["authmode"]); + } + $this->write_config(); + return APIResponse\get(0, $del_server); + } + + public function validate_payload() { + if (isset($this->initial_data['name'])) { + $this->validated_data["name"] = $this->initial_data['name']; + } else { + $this->errors[] = APIResponse\get(5010); + } + + // Loop through our servers and check that this server exists + foreach ($this->config["system"]["authserver"] as $asid => $asc) { + if ($this->validated_data["name"] === $asc["name"] and $asc["type"] === "radius") { + $this->validated_data["id"] = $asid; // Save our config before deleting + // Check if this auth server is our default, if so revert to local database + if ($this->validated_data["name"] === $this->config["system"]["webgui"]["authmode"]) { + $this->validated_data["revert"] = True; + } + } + } + if (is_null($this->validated_data["id"])) { + $this->errors[] = APIResponse\get(5025); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDeleteGroups.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDeleteGroups.inc new file mode 100644 index 000000000..e5e04f29e --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersDeleteGroups.inc @@ -0,0 +1,62 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-usermanager-addprivs", "page-system-groupmanager-addprivs"]; + $this->change_note = "Deleted group membership for user via API"; + } + + public function action() { + local_user_set_groups($this->validated_data["user_config"], $this->validated_data["new_groups"]); // Set our user's groups + local_user_set($this->validated_data["user_config"]); // Reset our user + $this->write_config(); // Write our config + // Update our current user's groups + $this->validated_data["user_config"] = getUserEntry($this->validated_data["username"]); + $this->validated_data["user_groups"] = local_user_get_groups($this->validated_data["user_config"], true); + return APIResponse\get(0, $this->validated_data["group"]); + } + + public function validate_payload() { + if (isset($this->initial_data['username'])) { + if (!array_key_exists($this->initial_data["username"], index_users())) { + $this->errors[] = APIResponse\get(5001); + } else { + $this->validated_data["username"] = $this->initial_data['username']; + $this->validated_data["username"] = trim($this->validated_data["username"]); + } + } else { + $this->errors[] = APIResponse\get(5000); + } + if (isset($this->initial_data['group'])) { + if (is_string($this->initial_data["group"])) { + $this->initial_data["group"] = array($this->initial_data["group"]); + } + $this->validated_data["group"] = $this->initial_data['group']; + } else { + $this->errors[] = APIResponse\get(5007); + } + + + if (is_array($this->validated_data["group"])) { + // Get our current user's groups + $this->validated_data["user_config"] = getUserEntry($this->validated_data["username"]); + $this->validated_data["user_groups"] = local_user_get_groups($this->validated_data["user_config"], true); + // Loop through our del group list and ensure it is valid + foreach ($this->validated_data["group"] as $dg) { + if (!array_key_exists($dg, index_groups())) { + $this->errors[] = APIResponse\get(5008); + } + if (in_array($dg, $this->validated_data["user_groups"])) { + $this->validated_data["new_groups"] = \array_diff($this->validated_data["user_groups"], array($dg)); + } + } + } else { + $this->errors[] = APIResponse\get(5009); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersModify.inc new file mode 100644 index 000000000..7751dce7e --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIUsersModify.inc @@ -0,0 +1,63 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-system-usermanager"]; + $this->change_note = "Modified user via API"; + } + + public function action() { + local_user_set($this->validated_data); + $this->write_config(); + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + if (isset($this->initial_data['username'])) { + $this->validated_data =& getUserEntry($this->initial_data['username']); + // Check that our user already exists + if (!array_key_exists("uid", $this->validated_data)) { + $this->errors[] = APIResponse\get(5001); + } + } else { + $this->errors[] = APIResponse\get(5000); + } + if (isset($this->initial_data['password'])) { + $password = $this->initial_data['password']; + $password = trim($password); + local_user_set_password($this->validated_data, $password); // Set our new user's password + } + 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 + + } + if (isset($this->initial_data['descr'])) { + $descr = $this->initial_data['descr']; + $descr = trim($descr); + $this->validated_data["descr"] = $descr; // Update our user's full name + } + if (isset($this->initial_data['expires'])) { + $expires = $this->initial_data['expires']; + $expires = trim($expires); + $this->validated_data["expires"] = $expires; // Update our user's expiration date + } + if (isset($this->initial_data['authorizedkeys'])) { + $authorizedkeys = $this->initial_data['authorizedkeys']; + $authorizedkeys = trim($authorizedkeys); + $this->validated_data["authorizedkeys"] = $authorizedkeys; // Update our user's authorized keys + } + if (isset($this->initial_data['ipsecpsk'])) { + $ipsecpsk = $this->initial_data['ipsecpsk']; + $ipsecpsk = trim($ipsecpsk); + $this->validated_data["ipsecpsk"] = $ipsecpsk; // Update our user's IPsec pre-shared key + } + $this->validated_data["scope"] = "user"; // Set our new user's system scope + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc index b961dfa7f..d8bdfa050 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc @@ -132,6 +132,91 @@ function get($id, $data=[]) { "return" => $id, "message" => "Could not locate system certificate" ], + // 2000-2999 reserved for /services API calls + 2000 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid sshd enable value" + ], + 2001 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid sshd key only mode" + ], + 2002 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid sshd agent forwarding value" + ], + 2003 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid sshd port" + ], + 2004 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override hostname required" + ], + 2005 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override domain required" + ], + 2006 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override IP address required" + ], + 2007 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override alias hostname required" + ], + 2008 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override alias domain required" + ], + 2009 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override alias already exists" + ], + 2010 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override already exists" + ], + 2011 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid unbound host override IP address" + ], + 2012 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override new IP required" + ], + 2013 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unbound host override does not exist" + ], 4000 => [ "status" => "bad request", @@ -728,6 +813,18 @@ function get($id, $data=[]) { "return" => $id, "message" => "Authentication server LDAP group member attribute required" ], + 5025 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Could not locate authentication server" + ], + 5026 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Authentication server name already in use" + ], ]; $response = $responses[$id]; diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index c90b7a7ba..401bea437 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -647,4 +647,16 @@ function get_arp_entry($search, $value) { } } return $arp_match; +} + +// Checks if an authentication server exists by name +function is_authentication_server($name) { + global $err_lib, $config; + foreach ($config["system"]["authserver"] as $as) { + $reserved_names = [$as["name"], "Local_Database", "local", "LOCAL", "Local"]; + if (in_array($name, $reserved_names)) { + return true; + } + } + return false; } \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/apicalls.inc b/pfSense-pkg-API/files/etc/inc/apicalls.inc index e14153b60..584ab2611 100644 --- a/pfSense-pkg-API/files/etc/inc/apicalls.inc +++ b/pfSense-pkg-API/files/etc/inc/apicalls.inc @@ -2891,7 +2891,7 @@ function api_users_delete_groups() { # VARIABLES global $err_lib, $userindex, $groupindex, $api_resp, $client_id, $client_params; $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-usermanager-addprivs", "page-system-groupmanager-"); // Allowed privs + $req_privs = array("page-all", "page-system-usermanager-addprivs", "page-system-groupmanager-addprivs"); // Allowed privs $userindex = index_users(); // Index our users $groupindex = index_groups(); // Index our groups $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method @@ -3332,7 +3332,7 @@ function api_users_add_groups() { # VARIABLES global $err_lib, $userindex, $groupindex, $api_resp, $client_id, $client_params; $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-usermanager-addprivs", "page-system-groupmanager-"); // Allowed privs + $req_privs = array("page-all", "page-system-groupmanager"); // Allowed privs $userindex = index_users(); // Index our users $groupindex = index_groups(); // Index our groups $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method @@ -4206,8 +4206,9 @@ function api_services_sshd_modify() { $read_only_action = false; // Set whether this action requires read only access $req_privs = array("page-all", "page-system-advanced-admin"); // Array of privileges allowing this action $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $allowed_auth_types = array("disabled", "enabled", "both"); // Array of allowed auth types $err_found = false; // Track errors + $allowed_auth_types = array("disabled", "enabled", "both"); // Array of allowed auth types + # RUN TIME // Check that client is authenticated and authorized if (api_authorized($req_privs, $read_only_action)) { diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/routing/gateways/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/routing/gateways/index.php index 865cf8972..41ee707a6 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/routing/gateways/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/routing/gateways/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/index.php index f8e5d60c8..2aaaa9559 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/restart/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/restart/index.php index f2f0d1030..7661f5cf1 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/restart/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/restart/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/start/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/start/index.php index 555fb7020..b33c66cbf 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/start/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/start/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/stop/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/stop/index.php index d309918fd..14635c650 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/stop/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/ntpd/stop/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/restart/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/restart/index.php index e82ff1aea..9153bf015 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/restart/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/restart/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/index.php index ec12c7b8a..01da72e23 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/modify/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/modify/index.php index 0c938c7c0..e0beba5af 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/modify/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/modify/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/restart/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/restart/index.php index 3b04b03ad..882573e39 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/restart/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/restart/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/start/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/start/index.php index 577bb5867..84e3c8e2a 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/start/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/start/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/stop/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/stop/index.php index 207fe094c..30e2aa0b1 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/stop/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/sshd/stop/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/start/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/start/index.php index 24678f038..1b5397b50 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/start/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/start/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/stop/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/stop/index.php index 95a344888..e6c49669a 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/stop/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/stop/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/restart/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/restart/index.php index b31a83578..942fd3667 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/restart/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/restart/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/start/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/start/index.php index 20443ce90..0e698f02f 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/start/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/start/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/stop/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/stop/index.php index 8cd137d9c..315036bf0 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/stop/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/syslogd/stop/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/groups/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/groups/index.php index 8a68a9c78..eca4ccb07 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/groups/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/groups/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/index.php index c162a2099..79b59a322 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/privs/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/privs/index.php index 6a7dae736..167c4fa48 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/privs/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/add/privs/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/delete/index.php index 9122fc6d8..412dfde62 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/index.php index ac7302099..56b3c59ae 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/add/index.php index de955cf5a..bf8f386dd 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/add/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/delete/index.php index d582905fb..095c67515 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/index.php index 8cbb4d208..e360be1d1 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/ldap/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/radius/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/radius/delete/index.php index 77f44ef26..31008e353 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/radius/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/radius/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/radius/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/radius/index.php index f5de43519..b1a076ec2 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/radius/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/authservers/radius/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/groups/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/groups/index.php index 8441b4e76..f7c5e8571 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/groups/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/delete/groups/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/users/modify/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/users/modify/index.php index a937f4568..5b2b904d0 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/users/modify/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/users/modify/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file From 67cd474cebca712acc66b5845426b625c1d84df7 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Mon, 24 Aug 2020 21:39:09 -0600 Subject: [PATCH 12/16] Migrating services to OOP --- .../api_models/APIServicesDHCPdRestart.inc | 17 +++ .../api/api_models/APIServicesDHCPdStart.inc | 17 +++ .../api/api_models/APIServicesDHCPdStop.inc | 17 +++ .../api_models/APIServicesDpingerRestart.inc | 17 +++ .../api_models/APIServicesDpingerStart.inc | 17 +++ .../api/api_models/APIServicesDpingerStop.inc | 17 +++ .../inc/api/api_models/APIServicesUnbound.inc | 21 +++ .../APIServicesUnboundDeleteHosts.inc | 144 ++++++++++++++++++ .../etc/inc/api/framework/APIResponse.inc | 8 +- .../files/etc/inc/api/framework/APITools.inc | 13 ++ .../api/v1/services/dhcpd/restart/index.php | 10 +- .../www/api/v1/services/dhcpd/start/index.php | 10 +- .../www/api/v1/services/dhcpd/stop/index.php | 10 +- .../api/v1/services/dpinger/restart/index.php | 10 +- .../api/v1/services/dpinger/start/index.php | 10 +- .../api/v1/services/dpinger/stop/index.php | 10 +- .../services/unbound/delete/hosts/index.php | 10 +- .../www/api/v1/services/unbound/index.php | 10 +- 18 files changed, 303 insertions(+), 65 deletions(-) create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdRestart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdStart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdStop.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerRestart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerStart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerStop.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnbound.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundDeleteHosts.inc diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdRestart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdRestart.inc new file mode 100644 index 000000000..c607427f2 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdRestart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_restart("dhcpd", []); + return APIResponse\get(0, ["dhcpd" => "restarted"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdStart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdStart.inc new file mode 100644 index 000000000..42db0c219 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdStart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_start("dhcpd", []); + return APIResponse\get(0, ["dhcpd" => "started"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdStop.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdStop.inc new file mode 100644 index 000000000..dbda6472c --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDHCPdStop.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_stop("dhcpd", []); + return APIResponse\get(0, ["dhcpd" => "stopped"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerRestart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerRestart.inc new file mode 100644 index 000000000..17a310a17 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerRestart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_restart("dpinger", []); + return APIResponse\get(0, ["dpinger" => "restarted"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerStart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerStart.inc new file mode 100644 index 000000000..2ffa9b5be --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerStart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_start("dpinger", []); + return APIResponse\get(0, ["dpinger" => "started"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerStop.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerStop.inc new file mode 100644 index 000000000..3d6d4dee1 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesDpingerStop.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_stop("dpinger", []); + return APIResponse\get(0, ["dpinger" => "stopped"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnbound.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnbound.inc new file mode 100644 index 000000000..1e83ab182 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnbound.inc @@ -0,0 +1,21 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-services-dnsresolver"]; + } + + public function action() { + $unbound_array = []; + if (!empty($this->config["unbound"])) { + $unbound_array = $this->config["unbound"]; + } + return APIResponse\get(0, $unbound_array); + } +} diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundDeleteHosts.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundDeleteHosts.inc new file mode 100644 index 000000000..84c526bb4 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundDeleteHosts.inc @@ -0,0 +1,144 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-services-dnsresolver-edithost"]; + $this->change_note = "Deleted DNS Resolver host override via API"; + } + + public function action() { + usort($this->validated_data["hosts_conf"], "strcmp"); + $this->config["unbound"]["hosts"] = $this->validated_data["hosts_conf"]; + $this->write_config(); + mark_subsystem_dirty("unbound"); + # If user requests immediately application + if ($this->validated_data["apply"] === true) { + APITools\unbound_reload_config(); + } + // Return success if our function was successful + return APIResponse\get(0, $this->validated_data["del_list"]); + } + + public function validate_payload() { + if (isset($this->initial_data['host'])) { + $this->validated_data["hostname"] = trim($this->initial_data['host']); + $h_mode = true; + } + if (isset($this->initial_data['domain'])) { + $this->validated_data["domain"] = trim($this->initial_data['domain']); + $d_mode = true; + } + if (isset($this->initial_data['ip'])) { + $this->validated_data["ip"] = trim($this->initial_data['ip']); + $i_mode = true; + } + if ($this->initial_data['aliases'] === true) { + $a_mode = true; + } + if ($this->initial_data['apply'] === true) { + $this->validated_data["apply"] = $this->initial_data['apply']; + } + // Determine criteria for deletion + if ($h_mode and !$d_mode and !$i_mode) { + $del_mode = "h"; + } elseif ($h_mode and $d_mode and !$i_mode) { + $del_mode = "hd"; + } elseif ($h_mode and !$d_mode and $i_mode) { + $del_mode = "hi"; + } elseif ($h_mode and $d_mode and $i_mode) { + $del_mode = "hdi"; + } elseif (!$h_mode and $d_mode and !$i_mode) { + $del_mode = "d"; + } elseif (!$h_mode and $d_mode and $i_mode) { + $del_mode = "di"; + } elseif (!$h_mode and !$d_mode and $i_mode) { + $del_mode = "i"; + } else { + $this->errors [] = APIResponse\get(2014); + } + + // Check that our configuration is a list and loop through each item, otherwise return ok resp + if (array_key_exists("hosts", $this->config["unbound"]) and is_array($this->config["unbound"]["hosts"])) { + $this->validated_data["del_list"] = array("hosts" => array(), "aliases" => array()); // List of deleted items + $this->validated_data["hosts_conf"] = &$this->config["unbound"]["hosts"]; // Current Unbound host overrides + $h_count = 0; // Define counter for our hosts loop + foreach ($this->validated_data["hosts_conf"] as $he) { + // Check aliases for match if alias mode + if ($a_mode and is_array($he["aliases"])) { + $a_count = 0; // Define counter for our aliases loop + // Loop through aliases to check for matches + foreach ($he["aliases"]["item"] as $ae) { + if ($del_mode === "h") { + if ($this->validated_data["hostname"] === $ae["host"]) { + unset($this->validated_data["hosts_conf"][$h_count]["aliases"]["item"][$a_count]); + $this->validated_data["del_list"]["aliases"][] = $ae["host"].".".$ae["domain"]; + } + } elseif ($del_mode === "d") { + if ($this->validated_data["domain"] === $ae["domain"]) { + unset($this->validated_data["hosts_conf"][$h_count]["aliases"]["item"][$a_count]); + $this->validated_data["del_list"]["aliases"][] = $ae["host"].".".$ae["domain"]; + } + } elseif ($del_mode === "hd") { + if ($this->validated_data["hostname"] === $ae["host"] and $this->validated_data["domain"] === $ae["domain"]) { + unset($this->validated_data["hosts_conf"][$h_count]["aliases"]["item"][$a_count]); + $this->validated_data["del_list"]["aliases"][] = $ae["host"].".".$ae["domain"]; + } + } + // If all aliases were removed, restore aliases key to empty string + if (empty($this->validated_data["hosts_conf"][$h_count]["aliases"]["item"])) { + $this->validated_data["hosts_conf"][$h_count]["aliases"] = ""; + } + // Increase our alias counter + $a_count++; + } + } + // Check parent host entries + if ($del_mode === "h") { + if ($this->validated_data["hostname"] === $he["host"]) { + unset($this->validated_data["hosts_conf"][$h_count]); + $this->validated_data["del_list"]["hosts"][] = $he["host"].".".$he["domain"]; + } + } elseif ($del_mode === "d") { + if ($this->validated_data["domain"] === $he["domain"]) { + unset($this->validated_data["hosts_conf"][$h_count]); + $this->validated_data["del_list"]["hosts"][] = $he["host"].".".$he["domain"]; + } + } elseif ($del_mode === "i") { + if ($this->validated_data["ip"] === $he["ip"]) { + unset($this->validated_data["hosts_conf"][$h_count]); + $this->validated_data["del_list"]["hosts"][] = $he["host"].".".$he["domain"]; + } + } elseif ($del_mode === "hd") { + if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"]) { + unset($this->validated_data["hosts_conf"][$h_count]); + $this->validated_data["del_list"]["hosts"][] = $he["host"].".".$he["domain"]; + } + } elseif ($del_mode === "hi") { + if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["ip"] === $he["ip"]) { + unset($this->validated_data["hosts_conf"][$h_count]); + $this->validated_data["del_list"]["hosts"][] = $he["host"].".".$he["domain"]; + } + } elseif ($del_mode === "di") { + if ($this->validated_data["domain"] === $he["domain"] and $this->validated_data["ip"] === $he["ip"]) { + unset($this->validated_data["hosts_conf"][$h_count]); + $this->validated_data["del_list"]["hosts"][] = $he["host"].".".$he["domain"]; + } + } elseif ($del_mode === "hdi") { + if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"] and $this->validated_data["ip"] === $he["ip"]) { + unset($this->validated_data["hosts_conf"][$h_count]); + $this->validated_data["del_list"]["hosts"][] = $he["host"].".".$he["domain"]; + } + } + // Increase our host counter + $h_count++; + } + } else { + $this->errors[] = APIResponse\get(2013); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc index d8bdfa050..b4bdfe6fb 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc @@ -217,7 +217,13 @@ function get($id, $data=[]) { "return" => $id, "message" => "Unbound host override does not exist" ], - + 2014 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Host override deletion criteria not met" + ], + // 4000-4999 reserved for /firewall API calls 4000 => [ "status" => "bad request", "code" => 400, diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index 401bea437..0eb7a7552 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -659,4 +659,17 @@ function is_authentication_server($name) { } } return false; +} + +// Reload our unbound configuration, restart associated services and clear config locks +function unbound_reload_config() { + $reload_unbound = 0; + $reload_unbound |= services_unbound_configure(); + // Check if application was successful + if ($reload_unbound === 0) { + system_resolvconf_generate(); // Update resolveconf + system_dhcpleases_configure(); // Update DHCPD + clear_subsystem_dirty("unbound"); + return true; + } } \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/restart/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/restart/index.php index 12213ea4f..429a4f37d 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/restart/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/restart/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/start/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/start/index.php index 93d909f9c..1a1565c0a 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/start/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/start/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/stop/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/stop/index.php index 5df475cea..39038a095 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/stop/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dhcpd/stop/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/restart/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/restart/index.php index 5bd77ae6f..75a689286 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/restart/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/restart/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/start/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/start/index.php index bd5e1b20f..0e42c5aec 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/start/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/start/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/stop/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/stop/index.php index 9bf411da6..92c6f8fb7 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/stop/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/dpinger/stop/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/delete/hosts/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/delete/hosts/index.php index 91c73084b..bb1e63e85 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/delete/hosts/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/delete/hosts/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/index.php index 856cfb3b6..fd42539d4 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file From 66a1656917cceddbd3893129a156603325ab0a23 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Tue, 25 Aug 2020 09:18:33 -0600 Subject: [PATCH 13/16] Converting unbound queries to object oriented design --- .../APIServicesUnboundModifyHosts.inc | 200 ++++++++++++++++++ .../files/etc/inc/api/framework/APITools.inc | 30 ++- .../services/unbound/modify/hosts/index.php | 10 +- 3 files changed, 231 insertions(+), 9 deletions(-) create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc new file mode 100644 index 000000000..2a3b6795f --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc @@ -0,0 +1,200 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-services-dnsresolver-edithost"]; + $this->change_note = "Modified DNS Resolver host override via API"; + } + + public function action() { + // Sort and write our new configuration + //usort($this->validated_data["hosts_conf"], "strcmp"); + $this->config["unbound"]["hosts"] = $this->validated_data["hosts_conf"]; + $this->write_config(); + mark_subsystem_dirty("unbound"); + # If user requests immediately application + if ($this->validated_data["apply"] === true) { + APITools\unbound_reload_config(); + } + return APIResponse\get(0, $this->validated_data["update_list"]); + } + + public function validate_payload() { + if (isset($this->initial_data['host'])) { + $this->validated_data["hostname"] = trim($this->initial_data['host']); + $h_mode = true; + } + if (isset($this->initial_data['new_host'])) { + $new_hostname = trim($this->initial_data['new_host']); + } + if (isset($this->initial_data['domain'])) { + $this->validated_data["domain"] = trim($this->initial_data['domain']); + } elseif ($h_mode) { + $this->errors[] = APIResponse\get(2005); + } + if (isset($this->initial_data['new_domain'])) { + $this->validated_data["new_domain"] = trim($this->initial_data['new_domain']); + } + if (isset($this->initial_data['ip']) and !$h_mode) { + $this->validated_data["ip"] = trim($this->initial_data['ip']); + $i_mode = true; + } + if (isset($this->initial_data['new_ip'])) { + $this->validated_data["new_ip"] = trim($this->initial_data['new_ip']); + } elseif ($i_mode) { + $this->errors[] = APIResponse\get(2012); + } + if (isset($this->initial_data['descr'])) { + $this->validated_data["descr"] = $this->initial_data['descr']; + } + if (isset($this->initial_data['aliases'])) { + $this->validated_data["aliases"] = $this->initial_data['aliases']; + } + if ($this->initial_data['apply'] === true) { + $this->validated_data["apply"] = $this->initial_data['apply']; + } + // Validate our input against our exist configuration + if (APITools\unbound_host_override_exists($this->validated_data["hostname"], $this->validated_data["domain"]) or $i_mode) { + $this->validated_data["hosts_conf"] = $this->config["unbound"]["hosts"]; // Current Unbound host overrides + $h_count = 0; // Assign a loop counter + $this->validated_data["update_list"] = array(); // Assign array to track which values were updated + // Check modification mode + if ($i_mode) { + if (is_ipaddrv4($this->validated_data["new_ip"]) or is_ipaddrv6($this->validated_data["new_ip"])) { + foreach ($this->validated_data["hosts_conf"] as $he) { + // If our IP matches, update our IP + if ($this->validated_data["ip"] === $he["ip"]) { + $this->validated_data["hosts_conf"][$h_count]["ip"] = $this->validated_data["new_ip"]; + $this->validated_data["update_list"][] = $this->validated_data["hosts_conf"][$h_count]; + } + // Increase our counter + $h_count++; + } + } else { + $this->errors[] = APIResponse\get(2011); + } + } elseif ($h_mode) { + foreach ($this->validated_data["hosts_conf"] as $he) { + $he_updated = false; + // Check if both our hostname and domain names were changed + if (isset($new_hostname) and isset($this->validated_data["new_domain"])) { + if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"]) { + if (!APITools\unbound_host_override_exists($new_hostname, $this->validated_data["new_domain"])) { + $this->validated_data["hosts_conf"][$h_count]["host"] = $new_hostname; + $this->validated_data["hosts_conf"][$h_count]["domain"] = $this->validated_data["new_domain"]; + $he_updated = true; + } else { + $this->errors[] = APIResponse\get(2010); + } + } + } elseif (isset($new_hostname)) { + if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"]) { + if (!APITools\unbound_host_override_exists($new_hostname, $he["domain"])) { + $this->validated_data["hosts_conf"][$h_count]["host"] = $new_hostname; + $he_updated = true; + } else { + $this->errors[] = APIResponse\get(2010); + } + } + } elseif (isset($this->validated_data["new_domain"])) { + if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"]) { + if (!APITools\unbound_host_override_exists($he["host"], $this->validated_data["new_domain"])) { + $this->validated_data["hosts_conf"][$h_count]["domain"] = $this->validated_data["new_domain"]; + $he_updated = true; + } else { + $this->errors[] = APIResponse\get(2010); + } + } + } + if (isset($this->validated_data["new_ip"])) { + if (is_ipaddrv4($this->validated_data["new_ip"]) or is_ipaddrv6($this->validated_data["new_ip"])) { + if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"]) { + $this->validated_data["hosts_conf"][$h_count]["ip"] = $this->validated_data["new_ip"]; + $he_updated = true; + } + } else { + $this->errors[] = APIResponse\get(2011); + } + } + if (isset($this->validated_data["descr"])) { + if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"]) { + $this->validated_data["hosts_conf"][$h_count]["descr"] = $this->validated_data["descr"]; + $he_updated = true; + } + } + if (isset($this->validated_data["aliases"])) { + // Check if we have more than one + if (count($this->validated_data["update_list"]) <= 1) { + $alias_fin = $this->unbound_parse_aliases($this->validated_data["aliases"]); // Parse our aliases + if ($alias_fin !== "") { + $this->validated_data["hosts_conf"][$h_count]["aliases"] = $alias_fin; + $he_updated = true; + } + } + } + // Check if our entry was updated, if so add it to our update list + if ($he_updated) { + $this->validated_data["update_list"][] = $this->validated_data["hosts_conf"][$h_count]; + } + // Increase our counter + $h_count++; + } + } + } else { + $this->errors[] = APIResponse\get(2013); + } + } + + // Check user's DNS Resolver (Unbound) host override alias input for errors + public function unbound_parse_aliases($aliases) { + $aliases_fin = ""; // Default our alias value to blank string + if (is_array($aliases)) { + $a_count = 0; + $aliases_fin = array(); + foreach ($aliases as $alias_ent) { + // Check that each alias has a valid hostname + if (array_key_exists("host", $alias_ent) and is_string($alias_ent["host"])) { + $aliases_fin[$a_count] = array("host" => trim($alias_ent["host"])); + } else { + $this->errors[] = APIResponse\get(2007); + } + // Check that each alias has a valid domain name + if (array_key_exists("domain", $alias_ent) and is_string($alias_ent["domain"])) { + $aliases_fin[$a_count]["domain"] = trim($alias_ent["domain"]); + } else { + $this->errors[] = APIResponse\get(2008); + } + // Check for alias description under descr key + if (array_key_exists("descr", $alias_ent) and is_string($alias_ent["descr"])) { + if (is_string($alias_ent["descr"])) { + $aliases_fin[$a_count]["description"] = $alias_ent["descr"]; + } + } + // Check for alias description under description key + if (array_key_exists("description", $alias_ent) and is_string($alias_ent["description"])) { + if (is_string($alias_ent["description"])) { + $aliases_fin[$a_count]["description"] = $alias_ent["description"]; + } + } + // Increase our counter + $a_count++; + } + // Check if our aliases already exist + foreach ($aliases_fin as $af) { + print_r($af); + if (APITools\unbound_host_override_exists($af["host"], $af["domain"]) === true) { + $this->errors[] = APIResponse\get(2009); + } + } + // Nest our aliases under the item key + $aliases_fin = array("item" => $aliases_fin); + } + // Return our parsed aliases + return $aliases_fin; + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index 0eb7a7552..63547b6c2 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -672,4 +672,32 @@ function unbound_reload_config() { clear_subsystem_dirty("unbound"); return true; } -} \ No newline at end of file +} + +// Check if a DNS Resolver (Unbound) host override already exists +function unbound_host_override_exists($hostname, $domain) { + // Local variables + global $err_lib, $config; + $curr_hosts = array(); + $host_exists = false; + // Check if host override already exists + if (array_key_exists("hosts", $config["unbound"])) { + $curr_hosts = $config["unbound"]["hosts"]; + } + foreach ($curr_hosts as $host_ent) { + if ($host_ent["host"] === $hostname and $host_ent["domain"] === $domain) { + $host_exists = true; + break; + } + if (is_array($host_ent["aliases"])) { + foreach ($host_ent["aliases"]["item"] as $alias_ent) { + if ($alias_ent["host"] === $hostname and $alias_ent["domain"] === $domain) { + $host_exists = true; + break; + } + } + } + } + return $host_exists; +} + diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/modify/hosts/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/modify/hosts/index.php index 39bfd7109..ce22a9ac5 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/modify/hosts/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/modify/hosts/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file From 4a2bdd1aee4953621fb73a5c41c8169d45272af7 Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Tue, 25 Aug 2020 23:48:16 -0600 Subject: [PATCH 14/16] Completed transition to OOP, starting docs and tests --- README.md | 52 +- .../docs/CONTRIBUTING.md | 0 pfSense-pkg-API/docs/index.rst | 68 +++ .../etc/inc/api/api_models/APIInterfaces.inc | 18 + .../inc/api/api_models/APIInterfacesAdd.inc | 553 ++++++++++++++++++ .../api/api_models/APIInterfacesDelete.inc | 91 +++ .../inc/api/api_models/APIInterfacesVLANs.inc | 22 + .../api/api_models/APIInterfacesVLANsAdd.inc | 76 +++ .../api_models/APIInterfacesVLANsDelete.inc | 53 ++ .../api_models/APIInterfacesVLANsModify.inc | 99 ++++ .../api_models/APIServicesUnboundAddHosts.inc | 107 ++++ .../api_models/APIServicesUnboundApply.inc | 22 + .../APIServicesUnboundModifyHosts.inc | 75 +-- .../api_models/APIServicesUnboundRestart.inc | 17 + .../api_models/APIServicesUnboundStart.inc | 17 + .../api/api_models/APIServicesUnboundStop.inc | 17 + .../inc/api/api_models/APISystemAPIErrors.inc | 17 + .../etc/inc/api/framework/APIResponse.inc | 344 ++++++++++- .../files/etc/inc/api/framework/APITools.inc | 234 ++++++++ .../local/www/api/v1/interfaces/add/index.php | 10 +- .../www/api/v1/interfaces/delete/index.php | 10 +- .../usr/local/www/api/v1/interfaces/index.php | 10 +- .../www/api/v1/interfaces/vlans/add/index.php | 10 +- .../api/v1/interfaces/vlans/delete/index.php | 10 +- .../www/api/v1/interfaces/vlans/index.php | 10 +- .../api/v1/interfaces/vlans/modify/index.php | 10 +- .../v1/services/unbound/add/hosts/index.php | 10 +- .../api/v1/services/unbound/apply/index.php | 10 +- .../api/v1/services/unbound/restart/index.php | 10 +- .../api/v1/services/unbound/start/index.php | 10 +- .../api/v1/services/unbound/stop/index.php | 12 +- .../www/api/v1/system/api/errors/index.php | 7 +- 32 files changed, 1831 insertions(+), 180 deletions(-) rename CONTRIBUTING.md => pfSense-pkg-API/docs/CONTRIBUTING.md (100%) create mode 100644 pfSense-pkg-API/docs/index.rst create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfaces.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANs.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsAdd.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsDelete.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsModify.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundAddHosts.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundApply.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundRestart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundStart.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundStop.inc create mode 100644 pfSense-pkg-API/files/etc/inc/api/api_models/APISystemAPIErrors.inc diff --git a/README.md b/README.md index c57950c30..ee3a54285 100644 --- a/README.md +++ b/README.md @@ -1,34 +1,68 @@ --- # Introduction -pfSense API is a fast, safe, full-fledged HTTP API. This works by leveraging the same PHP functions and processes used by pfSense's webConfigurator into API endpoints to create, read, update and delete pfSense configurations. All API endpoints enforce input validation to prevent invalid configurations from being made. Configurations made via API are properly written to the master XML configuration and the correct backend configurations are made preventing the need for a reboot. All this results in the fastest, safest, and easiest way to automate pfSense! +pfSense API is a fast, safe, full-fledged HTTP API. This works by leveraging the same PHP functions and processes used +by pfSense's webConfigurator into API endpoints to create, read, update and delete pfSense configurations. All API +endpoints enforce input validation to prevent invalid configurations from being made. Configurations made via API are +properly written to the master XML configuration and the correct backend configurations are made preventing the need for + a reboot. All this results in the fastest, safest, and easiest way to automate pfSense! # Installation To install pfSense API, simply run the following command from the pfSense shell:
-`pkg add https://github.com/jaredhendrickson13/pfsense-api/releases/download/v0.0.2/pfSense-2-4-pkg-API-0.0_2.txz`
+`pkg add https://github.com/jaredhendrickson13/pfsense-api/releases/download/v0.0.3/pfSense-2-4-pkg-API-0.0_3.txz`
To uninstall, run the following command:
`pkg delete pfSense-pkg-API`
-_Note: if you do not have shell access to pfSense, you can still install via the webConfigurator by navigating to 'Diagnostics > Command Prompt' and enter the commands there_ +_Note: if you do not have shell access to pfSense, you can still install via the webConfigurator by navigating to +'Diagnostics > Command Prompt' and enter the commands there_ # Requirements - pfSense 2.4.4 or later is supported -- pfSense API requires a local user account in pfSense. The same permissions required to make configurations in the webConfigurator are required to make calls to the API endpoints -- While not an enforced requirement, it is STRONGLY recommended that you configure pfSense to use HTTPS instead of HTTP. This ensures that login credentials and/or API tokens remain secure in-transit +- pfSense API requires a local user account in pfSense. The same permissions required to make configurations in the +webConfigurator are required to make calls to the API endpoints +- While not an enforced requirement, it is STRONGLY recommended that you configure pfSense to use HTTPS instead of HTTP. + This ensures that login credentials and/or API tokens remain secure in-transit # Authentication -By default, pfSense API uses the same credentials as the webConfigurator. This behavior allows you to configure pfSense from the API out of the box, and user passwords may be changed from the API to immediately add additional security if needed. Alternatively, you can configure pfSense API to create secure API client IDs and tokens for API users. To generate, or delete API keys you can navigate to `System > API` in the UI after installation, and change the authentication mode to `API Token`. +By default, pfSense API uses the same credentials as the webConfigurator. This behavior allows you to configure pfSense +from the API out of the box, and user passwords may be changed from the API to immediately add additional security if +needed. After installation, you can navigate to System > API in the pfSense webConfigurator to configure API +authentication. + +To authenticate your API call, follow the instructions for your configured authentication mode: + +- Local Database (default) : Uses the same credentials as the pfSense webConfigurator. To authenticate API calls, simply +add a `client-id` value containing your username and a `client-token` value containing your password to your payload. For +example `{"client-id": "admin", "client-token": "pfsense"}` + +- JWT : Requires a bearer token to be included in the `Authorization` header of your request. To receive a bearer token, +you may make a POST request to /api/v1/access_token/ and include a `client-id` value containing your pfSense username +and a `client-token` value containing your pfSense password to your payload. For example +`{"client-id": "admin", "client-token": "pfsense"}`. Once you have your bearer token, you can authenticate your API +call by adding it to the request's authorization header. (e.g. `Authorization: Bearer xxxxxxxx.xxxxxxxxx.xxxxxxxx`) + +- API Token : Uses standalone tokens generated via the UI. These are better suited to distribute to systems as they are +revocable and will only allow API authentication and not UI or SSH authentication (like the local database credentials). +To generate or revoke credentials, navigate to System > API within the UI and ensure the Authentication Mode is set to +API token. Then you should have the options to configure API Token generation, generate new tokens, and revoke existing +tokens. Once you have your API token, you may authenticate your API call by adding a `client-id` value containing your +API token client ID and a `client-token` value containing your API token client token to your payload. +(e.g. `{"client-id": "cccdj-311s", "client-token": "42jkjl-k234jlk1b38123kj3kjl-ffwzzuilaei"}` # Response Codes `200 (OK)` : API call succeeded
`400 (Bad Request)` : An error was found within your requested parameters
-`401 (Unauthorized)` : API client has not completed authentiation or authorization successfully
+`401 (Unauthorized)` : API client has not completed authentication or authorization successfully
`403 (Forbidden)` : The API endpoint has refused your call. Commonly due to your access settings found in `System > API`
`404 (Not found)` : Either the API endpoint or requested data was not found
`500 (Server error)` : The API endpoint encountered an unexpected error processing your API request
# Error Codes -A full list of error codes can be found by navigating to /api/v1/system/api/errors/ after installation. This will return JSON data containing each error code and their corresponding error message. No authentication is required to view the error code library. This also makes API integration with third-party software easy as the API error codes and messages are always just an HTTP call away! +A full list of error codes can be found by navigating to /api/v1/system/api/errors/ after installation. This will return + JSON data containing each error code and their corresponding error message. No authentication is required to view the + error code library. This also makes API integration with third-party software easy as the API error codes and messages + are always just an HTTP call away! # Rate limit -There is no limit to API calls at this time +There is no limit to API calls at this time. Alternatively, you enable the API in read-only mode to only allow endpoints +with read access within System > API. diff --git a/CONTRIBUTING.md b/pfSense-pkg-API/docs/CONTRIBUTING.md similarity index 100% rename from CONTRIBUTING.md rename to pfSense-pkg-API/docs/CONTRIBUTING.md diff --git a/pfSense-pkg-API/docs/index.rst b/pfSense-pkg-API/docs/index.rst new file mode 100644 index 000000000..16fb3e709 --- /dev/null +++ b/pfSense-pkg-API/docs/index.rst @@ -0,0 +1,68 @@ +--- +# Introduction +pfSense API is a fast, safe, full-fledged HTTP API. This works by leveraging the same PHP functions and processes used +by pfSense's webConfigurator into API endpoints to create, read, update and delete pfSense configurations. All API +endpoints enforce input validation to prevent invalid configurations from being made. Configurations made via API are +properly written to the master XML configuration and the correct backend configurations are made preventing the need for + a reboot. All this results in the fastest, safest, and easiest way to automate pfSense! + +# Installation +To install pfSense API, simply run the following command from the pfSense shell:
+`pkg add https://github.com/jaredhendrickson13/pfsense-api/releases/download/v0.0.3/pfSense-2-4-pkg-API-0.0_3.txz`
+ +To uninstall, run the following command:
+`pkg delete pfSense-pkg-API`
+ +_Note: if you do not have shell access to pfSense, you can still install via the webConfigurator by navigating to +'Diagnostics > Command Prompt' and enter the commands there_ + +# Requirements +- pfSense 2.4.4 or later is supported +- pfSense API requires a local user account in pfSense. The same permissions required to make configurations in the +webConfigurator are required to make calls to the API endpoints +- While not an enforced requirement, it is STRONGLY recommended that you configure pfSense to use HTTPS instead of HTTP. + This ensures that login credentials and/or API tokens remain secure in-transit + +# Authentication +By default, pfSense API uses the same credentials as the webConfigurator. This behavior allows you to configure pfSense +from the API out of the box, and user passwords may be changed from the API to immediately add additional security if +needed. After installation, you can navigate to System > API in the pfSense webConfigurator to configure API +authentication. + +To authenticate your API call, follow the instructions for your configured authentication mode: + +- Local Database (default) : Uses the same credentials as the pfSense webConfigurator. To authenticate API calls, simply +add a `client-id` value containing your username and a `client-token` value containing your password to your payload. For +example `{"client-id": "admin", "client-token": "pfsense"}` + +- JWT : Requires a bearer token to be included in the `Authorization` header of your request. To receive a bearer token, +you may make a POST request to /api/v1/access_token/ and include a `client-id` value containing your pfSense username +and a `client-token` value containing your pfSense password to your payload. For example +`{"client-id": "admin", "client-token": "pfsense"}`. Once you have your bearer token, you can authenticate your API +call by adding it to the request's authorization header. (e.g. `Authorization: Bearer xxxxxxxx.xxxxxxxxx.xxxxxxxx`) + +- API Token : Uses standalone tokens generated via the UI. These are better suited to distribute to systems as they are +revocable and will only allow API authentication and not UI or SSH authentication (like the local database credentials). +To generate or revoke credentials, navigate to System > API within the UI and ensure the Authentication Mode is set to +API token. Then you should have the options to configure API Token generation, generate new tokens, and revoke existing +tokens. Once you have your API token, you may authenticate your API call by adding a `client-id` value containing your +API token client ID and a `client-token` value containing your API token client token to your payload. +(e.g. `{"client-id": "cccdj-311s", "client-token": "42jkjl-k234jlk1b38123kj3kjl-ffwzzuilaei"}` + +# Response Codes +`200 (OK)` : API call succeeded
+`400 (Bad Request)` : An error was found within your requested parameters
+`401 (Unauthorized)` : API client has not completed authentication or authorization successfully
+`403 (Forbidden)` : The API endpoint has refused your call. Commonly due to your access settings found in `System > API`
+`404 (Not found)` : Either the API endpoint or requested data was not found
+`500 (Server error)` : The API endpoint encountered an unexpected error processing your API request
+ +# Error Codes +A full list of error codes can be found by navigating to /api/v1/system/api/errors/ after installation. This will return + JSON data containing each error code and their corresponding error message. No authentication is required to view the + error code library. This also makes API integration with third-party software easy as the API error codes and messages + are always just an HTTP call away! + +# Rate limit +There is no limit to API calls at this time. Alternatively, you enable the API in read-only mode to only allow endpoints +with read access within System > API. diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfaces.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfaces.inc new file mode 100644 index 000000000..369782e14 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfaces.inc @@ -0,0 +1,18 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-interfaces-assignnetworkports"]; + + } + + public function action() { + return APIResponse\get(0, $this->config["interfaces"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesAdd.inc new file mode 100644 index 000000000..1ef197f64 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesAdd.inc @@ -0,0 +1,553 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-interfaces-assignnetworkports"]; + $this->change_note = "Added interface via API"; + + } + + public function action() { + $this->config["interfaces"] = array_merge($this->config["interfaces"], $this->validated_data); + $this->write_config(); + $apply_if = APITools\apply_interface_config($this->validated_data); + if ($apply_if) { + return APIResponse\get(0, $this->validated_data); + } else { + return APIResponse\get(1); + } + } + + public function validate_payload() { + $if_list = APITools\get_all_avail_interfaces(); // Save all our available interfaces + $allowed_ip4_types = array("staticv4", "dhcp"); // List of allowed IPv4 if types + $allowed_ip6_types = array("staticv6", "dhcp6", "slaac", "6rd", "6to4", "track6"); // List of allowed IPv6 if types + $next_if = APITools\get_next_pfsense_if_id(); + $this->validated_data = array($next_if => []); + if (isset($this->initial_data["if"])) { + $interface = trim($this->initial_data["if"]); + // Check that our interface exists and is not in use + if (!array_key_exists($interface, $if_list)) { + $this->errors[] = APIResponse\get(3000); + } elseif (isset($if_list[$interface]["in_use"])) { + $this->errors[] = APIResponse\get(3001); + } + $this->validated_data[$next_if]["if"] = $interface; + } else { + $this->errors[] = APIResponse\get(3002); + } + // Check for our enable value + if (isset($this->initial_data["enable"])) { + $enable = true; + $this->validated_data[$next_if]["enable"] = ""; + } + // Check for our MAC address value + if (isset($this->initial_data["spoofmac"])) { + $mac_addr = $this->initial_data["spoofmac"]; + // Check if mac addr is valid + if (is_macaddr($mac_addr)) { + $this->validated_data[$next_if]["spoofmac"] = $mac_addr; + } else { + $this->errors[] = APIResponse\get(3003); + } + } + // Check for our MTU value + if (isset($this->initial_data["mtu"])) { + $mtu = intval($this->initial_data["mtu"]); + // Check if MTU is within range + if (1280 > $mtu or $mtu > 8192) { + $this->errors[] = APIResponse\get(3004); + } elseif ($if_list[$interface]["is_vlan"]) { + // Check if interface is VLAN and that it's MTU is lower than it's parent interface + $parent_if = $if_list[$interface]["if"]; + if ($mtu > $parent_if["mtu"]) { + $this->errors[] = APIResponse\get(3006); + } + } else { + $this->validated_data[$next_if]["mtu"] = $mtu; + } + } + // Check for our MSS value + if (isset($this->initial_data["mss"])) { + $mss = intval($this->initial_data["mss"]); + // Check if MSS is within range + if (576 > $mss or $mss > 65535) { + $this->errors[] = APIResponse\get(3005); + + } else { + $this->validated_data[$next_if]["mss"] = $mss; + } + } + // Check for our SPEED/DUPLEX value + if (isset($this->initial_data["media"])) { + $media = $this->initial_data["media"]; + $avail_media = APITools\get_if_media_options($interface, true); + // Loop each of our media options and see if our input matches + foreach ($avail_media as $mopt) { + if ($media === $mopt) { + $media_found = true; + $mopt_list = explode(" ", $mopt); + $this->validated_data[$next_if]["media"] = $mopt_list[0]; + $this->validated_data[$next_if]["mediaopt"] = $mopt_list[1]; + break; + } + } + // If we did not find a match return error + if (!$media_found) { + $this->errors[] = APIResponse\get(3007); + } + } + // Check for our description value + if (isset($this->initial_data["descr"])) { + $descr = APITools\sanitize_str($this->initial_data["descr"]); + // Check that is interface descriptive name does not alrady exist + if (!APITools\get_pfsense_if_id($descr)) { + $this->validated_data[$next_if]["descr"] = $descr; + } else { + $this->errors[] = APIResponse\get(3008); + } + } else { + $descr = strtoupper($next_if); + $this->validated_data[$next_if]["descr"] = $descr; + } + // Check for our block private IP value + if (isset($this->initial_data["blockpriv"])) { + $block_priv = true; + $this->validated_data[$next_if]["blockpriv"] = ""; + } + // Check for our block private IP value + if (isset($this->initial_data["blockbogons"])) { + $block_bogons = true; + $this->validated_data[$next_if]["blockbogons"] = ""; + } + // Check if we have an IPv4 configuration + if (isset($this->initial_data["type"])) { + $type = $this->initial_data["type"]; + // Check if our IPv4 config type is allowed + if (in_array($type, $allowed_ip4_types)) { + // Gather input for our various IPv4 interface configuration types + // IPv4 STATICV4 TYPE + if ($type === "staticv4") { + // Check if our IP is set + if (isset($this->initial_data["ipaddr"])) { + $ipaddr = $this->initial_data["ipaddr"]; + // Check if IP address is valid + if (!is_ipaddrv4($ipaddr)) { + $this->errors[] = APIResponse\get(3010); + } elseif (APITools\is_ip_in_use($ipaddr)) { + $this->errors[] = APIResponse\get(3009); + } else { + $this->validated_data[$next_if]["ipaddr"] = $ipaddr; + } + } else { + $this->errors[] = APIResponse\get(3011); + } + // Check if our subnet is valid + if (isset($this->initial_data["subnet"])) { + $subnet = strval($this->initial_data["subnet"]); + // Check if our subnet is within range + if (!is_subnet($ipaddr."/".$subnet)) { + $this->errors[] = APIResponse\get(3012); + } else { + $this->validated_data[$next_if]["subnet"] = $subnet; + } + } else { + // Update our message if we did not already encounter an error + $this->errors[] = APIResponse\get(3013); + + } + // Check if user specified a network gateway, if so check if it's valid + if (isset($this->initial_data["gateway"])) { + $gateway = $this->initial_data["gateway"]; + // Check if this gateway exists + if (!APITools\is_gateway($gateway)) { + $this->errors[] = APIResponse\get(3014); + } else { + $this->validated_data[$next_if]["gateway"] = $gateway; + } + } + // IPv4 DHCP TYPE + } elseif ($type === "dhcp") { + $this->validated_data[$next_if]["ipaddr"] = $type; // Set our ipaddr value to dhcp + // Check if we have a dhcphostname value + if (isset($this->initial_data["dhcphostname"])) { + $this->validated_data[$next_if]["dhcphostname"] = strval($this->initial_data["dhcphostname"]); + } + // Check if we have a alias-address value + if (isset($this->initial_data["alias-address"])) { + if (is_ipaddrv4($this->initial_data["alias-address"])) { + $this->validated_data[$next_if]["alias-address"] = strval($this->initial_data["alias-address"]); + if (isset($this->initial_data["alias-subnet"])) { + $dhcpaliasnet = str($this->initial_data["alias-subnet"]); + if (is_subnet($this->validated_data[$next_if]["alias-address"]."/".$dhcpaliasnet)) { + $this->validated_data[$next_if]["alias-subnet"] = $dhcpaliasnet; + } + } else { + $this->validated_data[$next_if]["alias-subnet"] = 32; + } + } else { + $this->errors[] = APIResponse\get(3015); + } + } + // Check if we have a dhcprejectfrom value + if (isset($this->initial_data["dhcprejectfrom"])) { + $dhcpreject = $this->initial_data["dhcprejectfrom"]; + // Check what data type was passed in + if (is_string($dhcpreject)) { + $dhcprejectlist = explode(",", $dhcpreject); + // Loop through our reject list and ensure values are valid + foreach ($dhcprejectlist as $ra) { + if (!is_ipaddrv4($ra)) { + $bad_reject = true; + break; + } + } + } elseif (is_array($dhcpreject)) { + // Loop through our reject list and ensure values are valid + foreach ($dhcpreject as $ra) { + if (!is_ipaddrv4($ra)) { + $bad_reject = true; + break; + } + } + // Convert our list to comma separated string + $dhcpreject = implode(",", $dhcpreject); + } + // Check for bad IPs + if ($bad_reject) { + $this->errors[] = APIResponse\get(3016); + } else { + $this->validated_data[$next_if]["dhcprejectfrom"] = $dhcpreject; + } + } + // Check for our DHCP protocol timing + $timing_protocols = array( + "adv_dhcp_pt_timeout" => ["keyword" => "timeout", "return" => 134, "min" => 1], + "adv_dhcp_pt_retry" => ["keyword" => "retry", "return" => 135, "min" => 1], + "adv_dhcp_pt_select_timeout" => ["keyword" => "select timeout", "return" => 136, "min" => 0], + "adv_dhcp_pt_reboot" => ["keyword" => "reboot", "return" => 137, "min" => 1], + "adv_dhcp_pt_backoff_cutoff" => ["keyword" => "backoff cutoff", "return" => 138, "min" => 1], + "adv_dhcp_pt_initial_interval" => ["keyword" => "initial interval", "return" => 139, "min" => 1], + ); + // Loop through each timing attribute and see if it's valid + foreach ($timing_protocols as $tp => $data) { + if (isset($this->initial_data[$tp])) { + // Check that value is in range + $dhcp_attr = intval($this->initial_data[$tp]); + if ($dhcp_attr >= $data["min"]) { + $this->validated_data[$next_if][$tp] = $dhcp_attr; + $this->validated_data[$next_if]["adv_dhcp_pt_values"] = "SavedCfg"; + } else { + if ($data["keyword"] === "timeout") { + $this->errors[] = APIResponse\get(3017); + } elseif ($data["keyword"] === "retry") { + $this->errors[] = APIResponse\get(3018); + } elseif ($data["keyword"] === "select timeout") { + $this->errors[] = APIResponse\get(3019); + } elseif ($data["keyword"] === "reboot") { + $this->errors[] = APIResponse\get(3020); + } elseif ($data["keyword"] === "backoff cutoff") { + $this->errors[] = APIResponse\get(3021); + } elseif ($data["keyword"] === "initial interval") { + $this->errors[] = APIResponse\get(3022); + } + } + } + } + // Check for advance DHCP config + if (isset($this->initial_data["adv_dhcp_config_advanced"])) { + $this->validated_data[$next_if]["adv_dhcp_config_advanced"] = "yes"; + // Check for our DHCP options + $dhcp_opts = array( + "adv_dhcp_send_options", + "adv_dhcp_request_options", + "adv_dhcp_required_options", + "adv_dhcp_option_modifiers" + ); + foreach ($dhcp_opts as $do) { + // Check if option exists + if (isset($this->initial_data[$do])) { + $this->validated_data[$next_if][$do] = strval($this->initial_data[$do]); + } + } + } + // Check for DHCP configuration file override option + if (isset($this->initial_data["adv_dhcp_config_file_override"])) { + $this->validated_data[$next_if]["adv_dhcp_config_file_override"] = ""; + // Check if a file path was given + if (isset($this->initial_data["adv_dhcp_config_file_override_path"])) { + $dhcp_conf_file = $this->initial_data["adv_dhcp_config_file_override_path"]; + // Check that our conf file exists + if (is_file($dhcp_conf_file)) { + $this->validated_data[$next_if]["adv_dhcp_config_file_override"] = $dhcp_conf_file; + } else { + $this->errors[] = APIResponse\get(3023); + } + } + } + // Check for DHCP VLAN priority + $dhcp_vlan_prios = array( + 0 => "bk", + 1 => "be", + 2 => "ee", + 3 => "ca", + 4 => "vi", + 5 => "vo", + 6 => "ic", + 7 => "nc" + ); + if (isset($this->initial_data["dhcpvlanenable"])) { + $this->validated_data[$next_if]["dhcpvlanenable"] = ""; + if (isset($this->initial_data["dhcpcvpt"])) { + $vlan_prio = strtolower($this->initial_data["dhcpcvpt"]); + // Check if VLAN priority was provided as number + if (is_numeric($vlan_prio) and array_key_exists(intval($vlan_prio), $dhcp_vlan_prios)) { + $this->validated_data[$next_if]["dhcpcvpt"] = $dhcp_vlan_prios[intval($vlan_prio)]; + } else { + // Loop through our priorities and see if value matches + foreach ($dhcp_vlan_prios as $dvp => $dvpval) { + if ($vlan_prio === $dvpval) { + $vlan_prio_found = true; + $this->validated_data[$next_if]["dhcpcvpt"] = $dvpval; + break; + } + } + // Check that we found a value in our loop + if (!$vlan_prio_found) { + $this->errors[] = APIResponse\get(3024); + } + } + } + } + } + } else { + $this->errors[] = APIResponse\get(3025); + } + } + // Check if we have an IPv6 configuration + if (isset($this->initial_data["type6"])) { + $type6 = $this->initial_data["type6"]; + // Check if our IPv6 config type is allowed + if (in_array($type6, $allowed_ip6_types)) { + // Gather input for our various IPv6 interface configuration types + // IPv6 STATICV6 TYPE + if ($type6 === "staticv6") { + // Check if our IP is set + if (isset($this->initial_data["ipaddrv6"])) { + $ipaddrv6 = $this->initial_data["ipaddrv6"]; + // Check if IP address is valid + if (!is_ipaddrv6($ipaddrv6)) { + $this->errors[] = APIResponse\get(3026); + } elseif (APITools\is_ip_in_use($ipaddrv6)) { + $this->errors[] = APIResponse\get(3027); + } else { + $this->validated_data[$next_if]["ipaddrv6"] = $ipaddrv6; + } + } else { + $this->errors[] = APIResponse\get(3028); + } + // Check if our subnet is valid + if (isset($this->initial_data["subnetv6"])) { + $subnetv6 = strval($this->initial_data["subnetv6"]); + // Check if our subnet is within range + if (!is_subnet($ipaddrv6 . "/" . $subnetv6)) { + $this->errors[] = APIResponse\get(3029); + } else { + $this->validated_data[$next_if]["subnetv6"] = $subnetv6; + } + } else { + $this->errors[] = APIResponse\get(3030); + } + // Check if user specified a network gateway, if so check if it's valid + if (isset($this->initial_data["gatewayv6"])) { + $gatewayv6 = $this->initial_data["gatewayv6"]; + // Check if this gateway exists + if (!APITools\is_gateway($gatewayv6)) { + $this->errors[] = APIResponse\get(3031); + } else { + $this->validated_data[$next_if]["gatewayv6"] = $gatewayv6; + } + } + // Check if user set ipv6usev4iface value + if (isset($this->initial_data["ipv6usev4iface"])) { + $this->validated_data[$next_if]["ipv6usev4iface"] = ""; + } + // IPv6 DHCP6 TYPE + } elseif ($type6 === "dhcp6") { + $this->validated_data[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to dhcp6 + // Check if user set ipv6usev4iface value + if (isset($this->initial_data["ipv6usev4iface"])) { + $this->validated_data[$next_if]["ipv6usev4iface"] = ""; + } + // Check if user set dhcp6prefixonly value + if (isset($this->initial_data["dhcp6prefixonly"])) { + $this->validated_data[$next_if]["dhcp6prefixonly"] = ""; + } + // Check if user set dhcp6-ia-pd-send-hint value + if (isset($this->initial_data["dhcp6-ia-pd-send-hint"])) { + $this->validated_data[$next_if]["dhcp6-ia-pd-send-hint"] = ""; + } + // Check if user set dhcp6debug value + if (isset($this->initial_data["dhcp6debug"])) { + $this->validated_data[$next_if]["dhcp6debug"] = ""; + } + // Check if user set dhcp6withoutra value + if (isset($this->initial_data["dhcp6withoutra"])) { + $this->validated_data[$next_if]["dhcp6withoutra"] = ""; + } + // Check if user set dhcp6norelease value + if (isset($this->initial_data["dhcp6norelease"])) { + $this->validated_data[$next_if]["dhcp6norelease"] = ""; + } + // Check if user set dhcp6vlanenable value + if (isset($this->initial_data["dhcp6vlanenable"])) { + $this->validated_data[$next_if]["dhcp6vlanenable"] = ""; + } + // Check if user set dhcp6-ia-pd-len value + if (isset($this->initial_data["dhcp6-ia-pd-len"])) { + // Set array of allowed prefix delegation sizes and their config translation + $dhcp6_del_size = intval($this->initial_data["dhcp6-ia-pd-len"]); + $allowed_size = array( + 64 => 0, + 63 => 1, + 62 => 2, + 61 => 3, + 60 => 4, + 59 => 5, + 56 => 8, + 52 => 12, + 48 => 16 + ); + if (array_key_exists($dhcp6_del_size, $allowed_size)) { + $this->validated_data[$next_if]["dhcp6-ia-pd-len"] = $allowed_size[$dhcp6_del_size]; + } else { + $this->errors[] = APIResponse\get(3032); + } + } + // Check for DHCP VLAN priority + $dhcp_vlan_prios = array( + 0 => "bk", + 1 => "be", + 2 => "ee", + 3 => "ca", + 4 => "vi", + 5 => "vo", + 6 => "ic", + 7 => "nc" + ); + if (isset($this->initial_data["dhcp6vlanenable"])) { + $this->validated_data[$next_if]["dhcp6vlanenable"] = ""; + if (isset($this->initial_data["dhcp6cvpt"])) { + $vlan_prio = strtolower($this->initial_data["dhcp6cvpt"]); + // Check if VLAN priority was provided as number + if (is_numeric($vlan_prio) and array_key_exists(intval($vlan_prio), $dhcp_vlan_prios)) { + $this->validated_data[$next_if]["dhcp6cvpt"] = $dhcp_vlan_prios[intval($vlan_prio)]; + } else { + // Loop through our priorities and see if value matches + foreach ($dhcp_vlan_prios as $dvp => $dvpval) { + if ($vlan_prio === $dvpval) { + $vlan_prio_found = true; + $this->validated_data[$next_if]["dhcp6cvpt"] = $dvpval; + break; + } + } + // Check that we found a value in our loop + if (!$vlan_prio_found) { + $this->errors[] = APIResponse\get(3033); + } + } + } + } + // Check for DHCP configuration file override option + if (isset($this->initial_data["adv_dhcp6_config_file_override"])) { + $this->validated_data[$next_if]["adv_dhcp6_config_file_override"] = ""; + // Check if a file path was given + if (isset($this->initial_data["adv_dhcp6_config_file_override_path"])) { + $dhcp_conf_file = $this->initial_data["adv_dhcp6_config_file_override_path"]; + // Check that our conf file exists + if (is_file($dhcp_conf_file)) { + $this->validated_data[$next_if]["adv_dhcp6_config_file_override_path"] = $dhcp_conf_file; + } else { + $this->errors[] = APIResponse\get(3034); + } + } + } + // IPv6 SLAAC TYPE + } elseif ($type6 === "slaac") { + $this->validated_data[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to slaac + // IPv6 6RD TYPE + } elseif ($type6 === "6rd") { + $this->validated_data[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to 6rd + $this->validated_data[$next_if]["prefix-6rd-v4plen"] = $this->initial_data["prefix-6rd-v4plen"]; // Default prefix len + // Check for a 6RD border relay + if (isset($this->initial_data["gateway-6rd"])) { + $gw6rd = $this->initial_data["gateway-6rd"]; + // Check that our gateway is a valid IPv4 address + if (is_ipaddrv4($gw6rd)) { + $this->validated_data[$next_if]["gateway-6rd"] = $this->initial_data["gateway-6rd"]; + } else { + $this->errors[] = APIResponse\get(3035); + } + } else { + $this->errors[] = APIResponse\get(3036); + } + // Check for a 6RD prefix + if (isset($this->initial_data["prefix-6rd"])) { + $this->validated_data[$next_if]["prefix-6rd"] = $this->initial_data["prefix-6rd"]; + } + // Check for a 6RD prefix length + if (isset($this->initial_data["prefix-6rd-v4plen"])) { + $prefix_len = $this->initial_data["prefix-6rd-v4plen"]; + // Check if our prefix length is within range + if (is_numeric($prefix_len) and (0 <= intval($prefix_len) and intval($prefix_len) <= 32)) { + $this->validated_data[$next_if]["prefix-6rd-v4plen"] = $this->initial_data["prefix-6rd-v4plen"]; + } else { + $this->errors[] = APIResponse\get(3037); + } + } + // IPv6 TRACK TYPE + } elseif ($type6 === "track6") { + $this->validated_data[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to track6 + // Check for track 6 interface + if (isset($this->initial_data["track6-interface"])) { + $track_if = $this->initial_data["track6-interface"]; + $track_if = APITools\get_pfsense_if_id($track_if); + // Check that our gateway is a valid IPv4 address + if (array_key_exists($track_if, APITools\get_ipv6_if_list())) { + $this->validated_data[$next_if]["track6-interface"] = $this->initial_data["track6-interface"]; + } else { + $this->errors[] = APIResponse\get(3038); + } + } else { + $this->errors[] = APIResponse\get(3039); + } + // Check for track 6 prefix ID + $track_prefix = 0; // Default our prefix value + if (isset($this->initial_data["track6-prefix-id-hex"])) { + $track_prefix = $this->initial_data["track6-prefix-id-hex"]; + // Check that our gateway is a valid IPv4 address + if (is_numeric($track_prefix) and ctype_xdigit(strval($track_prefix))) { + $this->validated_data[$next_if]["track6-prefix-id--hex"] = intval($track_prefix); + } else { + $this->errors[] = APIResponse\get(3040); + } + } + // IPv6 6-to-4 TYPE + } elseif ($type6 === "6to4") { + $this->validated_data[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to 6to4 + } + } else { + $this->errors[] = APIResponse\get(3041); + + } + } + + } + +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesDelete.inc new file mode 100644 index 000000000..519d4ab89 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesDelete.inc @@ -0,0 +1,91 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-interfaces-assignnetworkports"]; + $this->change_note = "Deleted interface via API"; + + } + + public function action() { + $curr_config = [$this->validated_data["if"] => $this->config["interfaces"][$this->validated_data["if"]]]; + $del_stat = $this->destroy_interface($this->validated_data["if"]); // Destroy our interface + $this->write_config(); + return APIResponse\get($del_stat, ($del_stat === 0) ? $curr_config : []); + } + + public function validate_payload() { + if (isset($this->initial_data['if'])) { + if (empty(APITools\get_pfsense_if_id($this->initial_data['if']))) { + $this->errors[] = APIResponse\get(3000); + } else { + $this->validated_data["if"] = APITools\get_pfsense_if_id($this->initial_data['if']); + } + } else { + $this->errors[] = APIResponse\get(3002); + } + } + + // Delete an interface + private function destroy_interface($id) { + if ($id === "wan") { + $err_msg = 3042; + } elseif (link_interface_to_group($id)) { + $err_msg = 3043; + } elseif (link_interface_to_bridge($id)) { + $err_msg = 3044; + } elseif (link_interface_to_gre($id)) { + $err_msg = 3045; + } elseif (link_interface_to_gif($id)) { + $err_msg = 3046; + } elseif (interface_has_queue($id)) { + $err_msg = 3047; + } else { + unset($this->config['interfaces'][$id]['enable']); + $realid = get_real_interface($id); + interface_bring_down($id); // Bring down interface + unset($this->config['interfaces'][$id]); // Delete our interface from configuration + // Remove DHCP config for interface + if (is_array($this->config['dhcpd']) && is_array($this->config['dhcpd'][$id])) { + unset($this->config['dhcpd'][$id]); + services_dhcpd_configure('inet'); + } + // Removed interface config for dhcp6 + if (is_array($this->config['dhcpdv6']) && is_array($this->config['dhcpdv6'][$id])) { + unset($this->config['dhcpdv6'][$id]); + services_dhcpd_configure('inet6'); + } + // Remove ACL for interface + if (count($this->config['filter']['rule']) > 0) { + foreach ($this->config['filter']['rule'] as $x => $rule) { + if ($rule['interface'] == $id) { + unset($this->config['filter']['rule'][$x]); + } + } + } + // Remove NAT config for interface + if (is_array($this->config['nat']['rule']) && count($this->config['nat']['rule']) > 0) { + foreach ($this->config['nat']['rule'] as $x => $rule) { + if ($rule['interface'] == $id) { + unset($this->config['nat']['rule'][$x]['interface']); + } + } + } + + // Disable DHCP if last interface + if ($this->config['interfaces']['lan'] && $this->config['dhcpd']['wan']) { + unset($this->config['dhcpd']['wan']); + } + // Update VLAN assignments + link_interface_to_vlans($realid, "update"); + $err_msg = 0; + } + return $err_msg; + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANs.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANs.inc new file mode 100644 index 000000000..bb62f9a93 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANs.inc @@ -0,0 +1,22 @@ +methods = ["GET"]; + $this->privileges = ["page-all", "page-interfaces-vlan"]; + + } + + public function action() { + $vlan_array = []; + if (!empty($this->config["vlans"]["vlan"])) { + $vlan_array = $this->config["vlans"]["vlan"]; + } + return APIResponse\get(0, $vlan_array); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsAdd.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsAdd.inc new file mode 100644 index 000000000..6fb5fe23d --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsAdd.inc @@ -0,0 +1,76 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-interfaces-vlan-edit"]; + $this->change_note = "Added VLAN interface via API"; + + } + + public function action() { + $this->config["vlans"]["vlan"] = []; + $this->config["vlans"]["vlan"][] = $this->validated_data; // Write our configuration change + interface_vlan_configure($this->validated_data); // Configure our VLAN on the backend + $this->write_config(); + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + if (isset($this->initial_data['if'])) { + if (!does_interface_exist($this->initial_data['if'])) { + $this->errors[] = APIResponse\get(3051); + } else { + $this->validated_data["if"] = $this->initial_data['if']; + } + } else { + $this->errors[] = APIResponse\get(3055); + } + + if (isset($this->initial_data['tag'])) { + if (!is_numeric($this->initial_data['tag']) or (1 > intval($this->initial_data['tag']) or intval($this->initial_data['tag']) > 4096)) { + $this->errors[] = APIResponse\get(3052); + } else { + $this->validated_data["tag"] = intval(trim($this->initial_data['tag'])); + $str_tag = strval($this->validated_data["tag"]); + } + } else { + $this->errors[] = APIResponse\get(3048); + } + + if (isset($this->initial_data['pcp'])) { + if (0 > $this->initial_data['pcp'] or $this->initial_data['pcp'] > 7) { + $this->errors[] = APIResponse\get(3053); + } else { + $this->validated_data["pcp"] = intval(trim($this->initial_data['pcp'])); + } + } else { + $this->validated_data["pcp"] = ""; + } + + if (isset($this->initial_data['descr'])) { + $this->validated_data["descr"] = $this->initial_data['descr']; + } else { + $this->validated_data["descr"] = ""; + } + + // Check if our VLAN is already in use + if (is_array($this->config["vlans"]["vlan"])) { + foreach ($this->config["vlans"]["vlan"] as $vle) { + if ($this->validated_data["if"] === $vle["if"] and $str_tag === $vle["tag"]) { + $this->errors[] = APIResponse\get(3054); + } + } + } else { + $this->config["vlans"] = []; + $this->config["vlans"]["vlan"] = []; + } + $this->validated_data["vlanif"] = $this->validated_data["if"].".".$this->initial_data['tag']; + + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsDelete.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsDelete.inc new file mode 100644 index 000000000..4856cf6ed --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsDelete.inc @@ -0,0 +1,53 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-interfaces-vlan-edit"]; + $this->change_note = "Deleted interface VLAN via API"; + + } + + public function action() { + $del_ent = $this->config["vlans"]["vlan"][$this->validated_data["id"]]; // Save our deleted VLAN + pfSense_interface_destroy($this->config["vlans"]["vlan"][$this->validated_data["id"]]["vlanif"]); // delete our VLAN on the backend + unset($this->config["vlans"]["vlan"][$this->validated_data["id"]]); // Remove our VLAN configuration + $this->write_config(); + return APIResponse\get(0, $del_ent); + } + + public function validate_payload() { + $curr_vlans = $this->config["vlans"]["vlan"]; // Save our current VLANs + if (isset($this->initial_data['vlanif'])) { + $this->validated_data["vlanif"] = $this->initial_data['vlanif']; + } + elseif (isset($this->initial_data['id'])) { + $this->validated_data["id"] = $this->initial_data['id']; + } else { + $this->errors[] = APIResponse\get(3048); + } + // Ensure we have a vlanif and id regardless of which input selector was provided + if (isset($this->validated_data["vlanif"])) { + foreach ($curr_vlans as $ind => $cve) { + if ($this->validated_data["vlanif"] === $cve["vlanif"]) { + $this->validated_data["id"] = $ind; + break; + } + } + } else { + $this->validated_data["vlanif"] = $curr_vlans[$this->validated_data["id"]]["vlanif"]; + } + // Check that our interface is not in use currently + if (convert_real_interface_to_friendly_interface_name($this->validated_data["vlanif"])) { + $this->errors[] = APIResponse\get(3049); + } + if (empty($this->config["vlans"]["vlan"][$this->validated_data["id"]])) { + $this->errors[] = APIResponse\get(3050); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsModify.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsModify.inc new file mode 100644 index 000000000..a3f0d00f9 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIInterfacesVLANsModify.inc @@ -0,0 +1,99 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-interfaces-vlan-edit"]; + $this->change_note = "Modified VLAN interface via API"; + + } + + public function action() { + $vlan_ent =& $this->config["vlans"]["vlan"][$this->initial_data["id"]]; // Get current VLAN config + $vlan_ent = array_replace($vlan_ent, $this->validated_data); // Update our VLAN config + $vlan_ent["vlanif"] = $vlan_ent["if"].".".$vlan_ent["tag"]; // Update VLAN iface ID + + pfSense_interface_destroy($this->initial_data["vlanif"]); + interface_vlan_configure($vlan_ent); + // Check if we need to reassign an interface + $assigned_if = APITools\get_pfsense_if_id($this->initial_data["vlanif"]); + if (!empty($assigned_if)) { + $this->config['interfaces'][$assigned_if]['if'] = $vlan_ent["vlanif"]; // Write interface config + $this->write_config(); + interface_configure($assigned_if); // Configure our assigned interface + } + $this->write_config(); + return APIResponse\get(0, $vlan_ent); + } + + public function validate_payload() { + $if_list = get_interface_list(); // Get our interfaces list + $lagg_list = get_lagg_interface_list(); // Get our lagg interfaces list + $avail_ifs = $if_list + $lagg_list; // Combine the two lists + // Remove LAGG interface members as they cannot be assigned VLANs + foreach ($lagg_list as $lagg_if => $lagg) { + $lagg_members = explode(',', $lagg['members']); + foreach ($lagg_members as $lagm) { + if (isset($avail_ifs[$lagm])) { + unset($avail_ifs[$lagm]); + } + } + } + if (!isset($this->initial_data['vlanif'])) { + $this->errors[] = APIResponse\get(3048); + } + if (isset($this->initial_data['if'])) { + $this->validated_data["if"] = $this->initial_data['if']; + } + if (isset($this->initial_data['tag'])) { + $this->validated_data["tag"] = $this->initial_data['tag']; + $this->validated_data["tag"] = intval(trim($this->validated_data["tag"])); + $str_tag = strval($this->validated_data["tag"]); + } + if (isset($this->initial_data['pcp'])) { + $this->validated_data["pcp"] = $this->initial_data['pcp']; + $this->validated_data["pcp"] = intval(trim($this->validated_data["pcp"])); + } + if (isset($this->initial_data['descr'])) { + $this->validated_data["descr"] = $this->initial_data['descr']; + } + // Ensure we have a vlanif and id regardless of which input selector was provided + if (isset($this->initial_data["vlanif"])) { + foreach ($this->config["vlans"]["vlan"] as $ind => $cve) { + if ($this->initial_data["vlanif"] === $cve["vlanif"]) { + $this->initial_data["id"] = $ind; + break; + } + } + } else { + $this->initial_data["vlanif"] = $this->config["vlans"]["vlan"][$this->initial_data["id"]]["vlanif"]; + } + // Input validation + // Check if our parent interface exists + if (!array_key_exists($this->initial_data["id"], $this->config["vlans"]["vlan"])) { + $this->errors[] = APIResponse\get(3050); + } elseif (isset($this->validated_data["if"]) and !does_interface_exist($this->validated_data["if"])) { + $this->errors[] = APIResponse\get(3051); + } elseif (isset($this->validated_data["tag"]) and (1 > $this->validated_data["tag"] or $this->validated_data["tag"] > 4096)) { + $this->errors[] = APIResponse\get(3052); + } elseif (isset($this->validated_data["pcp"]) and (0 > $this->validated_data["pcp"] or $this->validated_data["pcp"] > 7)) { + $this->errors[] = APIResponse\get(3053); + } + // Check if our VLAN is already in use + if (is_array($this->config["vlans"]["vlan"])) { + if (isset($this->validated_data["if"]) and isset($this->validated_data["tag"])) { + foreach ($this->config["vlans"]["vlan"] as $vle) { + if ($this->validated_data["if"] === $vle["if"] and $str_tag === $vle["tag"]) { + $this->errors[] = APIResponse\get(3054); + + } + } + } + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundAddHosts.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundAddHosts.inc new file mode 100644 index 000000000..94d765cd8 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundAddHosts.inc @@ -0,0 +1,107 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-services-dnsresolver-edithost"]; + $this->change_note = "Added DNS Resolver host override via API"; + } + + public function action() { + $this->config["unbound"]["hosts"][] = $this->validated_data; + usort($this->config["unbound"]["hosts"], "host_cmp"); + $this->write_config(); + mark_subsystem_dirty("unbound"); + # If user requests immediately application + if ($this->initial_data['apply'] === true) { + $applied = unbound_reload_config(); + } + return APIResponse\get(0, $this->validated_data); + } + + public function validate_payload() { + if (isset($this->initial_data['host'])) { + $this->validated_data["host"] = trim($this->initial_data['host']); + } else { + $this->errors[] = APIResponse\get(2004); + } + if (isset($this->initial_data['domain'])) { + $this->validated_data["domain"] = trim($this->initial_data['domain']); + if (APITools\unbound_host_override_exists($this->validated_data["host"], $this->validated_data["domain"])) { + $this->errors[] = APIResponse\get(2010); + } + } else { + $this->errors[] = APIResponse\get(2005); + } + if (isset($this->initial_data['ip'])) { + if (!is_ipaddrv4($this->initial_data["ip"]) and !is_ipaddrv6($this->initial_data["ip"])) { + $this->errors[] = APIResponse\get(2011); + } else { + $this->validated_data["ip"] = trim($this->initial_data['ip']); + } + } else { + $this->errors[] = APIResponse\get(2006); + } + if (isset($this->initial_data['descr'])) { + $this->validated_data["descr"] = $this->initial_data['descr']; + } + if (isset($this->initial_data['aliases'])) { + $this->validated_data["aliases"] = $this->unbound_parse_aliases($this->initial_data['aliases']); + } + } + + // Check user's DNS Resolver (Unbound) host override alias input for errors + public function unbound_parse_aliases($aliases) { + $aliases_fin = ""; // Default our alias value to blank string + if (is_array($aliases)) { + $a_count = 0; + $aliases_fin = array(); + foreach ($aliases as $alias_ent) { + // Check that each alias has a valid hostname + if (array_key_exists("host", $alias_ent) and is_string($alias_ent["host"])) { + $aliases_fin[$a_count] = array("host" => trim($alias_ent["host"])); + } else { + $this->errors[] = APIResponse\get(2007); + } + // Check that each alias has a valid domain name + if (array_key_exists("domain", $alias_ent) and is_string($alias_ent["domain"])) { + $aliases_fin[$a_count]["domain"] = trim($alias_ent["domain"]); + } else { + $this->errors[] = APIResponse\get(2008); + } + // Check for alias description under descr key + if (array_key_exists("descr", $alias_ent) and is_string($alias_ent["descr"])) { + if (is_string($alias_ent["descr"])) { + $aliases_fin[$a_count]["description"] = $alias_ent["descr"]; + } + } + // Check for alias description under description key + if (array_key_exists("description", $alias_ent) and is_string($alias_ent["description"])) { + if (is_string($alias_ent["description"])) { + $aliases_fin[$a_count]["description"] = $alias_ent["description"]; + } + } + // Increase our counter + $a_count++; + } + // Check if our aliases already exist + foreach ($aliases_fin as $af) { + if (APITools\unbound_host_override_exists($af["host"], $af["domain"]) === true) { + $this->errors[] = APIResponse\get(2009); + } + } + // Nest our aliases under the item key + $aliases_fin = array("item" => $aliases_fin); + } + // Return our parsed aliases + return $aliases_fin; + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundApply.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundApply.inc new file mode 100644 index 000000000..9dc7b97aa --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundApply.inc @@ -0,0 +1,22 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-services-dnsresolver-edithost"]; + $this->change_note = "Added DNS Resolver host override via API"; + } + + public function action() { + // Check if application was successful + if (APITools\unbound_reload_config() === true) { + return APIResponse\get(0); + } else { + return APIResponse\get(1); + } + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc index 2a3b6795f..7453e109e 100644 --- a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc @@ -30,7 +30,7 @@ class APIServicesUnboundModifyHosts extends APIBaseModel { $h_mode = true; } if (isset($this->initial_data['new_host'])) { - $new_hostname = trim($this->initial_data['new_host']); + $this->validated_data["new_hostname"] = trim($this->initial_data['new_host']); } if (isset($this->initial_data['domain'])) { $this->validated_data["domain"] = trim($this->initial_data['domain']); @@ -52,9 +52,6 @@ class APIServicesUnboundModifyHosts extends APIBaseModel { if (isset($this->initial_data['descr'])) { $this->validated_data["descr"] = $this->initial_data['descr']; } - if (isset($this->initial_data['aliases'])) { - $this->validated_data["aliases"] = $this->initial_data['aliases']; - } if ($this->initial_data['apply'] === true) { $this->validated_data["apply"] = $this->initial_data['apply']; } @@ -82,20 +79,20 @@ class APIServicesUnboundModifyHosts extends APIBaseModel { foreach ($this->validated_data["hosts_conf"] as $he) { $he_updated = false; // Check if both our hostname and domain names were changed - if (isset($new_hostname) and isset($this->validated_data["new_domain"])) { + if (isset($this->validated_data["new_hostname"]) and isset($this->validated_data["new_domain"])) { if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"]) { - if (!APITools\unbound_host_override_exists($new_hostname, $this->validated_data["new_domain"])) { - $this->validated_data["hosts_conf"][$h_count]["host"] = $new_hostname; + if (!APITools\unbound_host_override_exists($this->validated_data["new_hostname"], $this->validated_data["new_domain"])) { + $this->validated_data["hosts_conf"][$h_count]["host"] = $this->validated_data["new_hostname"]; $this->validated_data["hosts_conf"][$h_count]["domain"] = $this->validated_data["new_domain"]; $he_updated = true; } else { $this->errors[] = APIResponse\get(2010); } } - } elseif (isset($new_hostname)) { + } elseif (isset($this->validated_data["new_hostname"])) { if ($this->validated_data["hostname"] === $he["host"] and $this->validated_data["domain"] === $he["domain"]) { - if (!APITools\unbound_host_override_exists($new_hostname, $he["domain"])) { - $this->validated_data["hosts_conf"][$h_count]["host"] = $new_hostname; + if (!APITools\unbound_host_override_exists($this->validated_data["new_hostname"], $he["domain"])) { + $this->validated_data["hosts_conf"][$h_count]["host"] = $this->validated_data["new_hostname"]; $he_updated = true; } else { $this->errors[] = APIResponse\get(2010); @@ -127,16 +124,6 @@ class APIServicesUnboundModifyHosts extends APIBaseModel { $he_updated = true; } } - if (isset($this->validated_data["aliases"])) { - // Check if we have more than one - if (count($this->validated_data["update_list"]) <= 1) { - $alias_fin = $this->unbound_parse_aliases($this->validated_data["aliases"]); // Parse our aliases - if ($alias_fin !== "") { - $this->validated_data["hosts_conf"][$h_count]["aliases"] = $alias_fin; - $he_updated = true; - } - } - } // Check if our entry was updated, if so add it to our update list if ($he_updated) { $this->validated_data["update_list"][] = $this->validated_data["hosts_conf"][$h_count]; @@ -149,52 +136,4 @@ class APIServicesUnboundModifyHosts extends APIBaseModel { $this->errors[] = APIResponse\get(2013); } } - - // Check user's DNS Resolver (Unbound) host override alias input for errors - public function unbound_parse_aliases($aliases) { - $aliases_fin = ""; // Default our alias value to blank string - if (is_array($aliases)) { - $a_count = 0; - $aliases_fin = array(); - foreach ($aliases as $alias_ent) { - // Check that each alias has a valid hostname - if (array_key_exists("host", $alias_ent) and is_string($alias_ent["host"])) { - $aliases_fin[$a_count] = array("host" => trim($alias_ent["host"])); - } else { - $this->errors[] = APIResponse\get(2007); - } - // Check that each alias has a valid domain name - if (array_key_exists("domain", $alias_ent) and is_string($alias_ent["domain"])) { - $aliases_fin[$a_count]["domain"] = trim($alias_ent["domain"]); - } else { - $this->errors[] = APIResponse\get(2008); - } - // Check for alias description under descr key - if (array_key_exists("descr", $alias_ent) and is_string($alias_ent["descr"])) { - if (is_string($alias_ent["descr"])) { - $aliases_fin[$a_count]["description"] = $alias_ent["descr"]; - } - } - // Check for alias description under description key - if (array_key_exists("description", $alias_ent) and is_string($alias_ent["description"])) { - if (is_string($alias_ent["description"])) { - $aliases_fin[$a_count]["description"] = $alias_ent["description"]; - } - } - // Increase our counter - $a_count++; - } - // Check if our aliases already exist - foreach ($aliases_fin as $af) { - print_r($af); - if (APITools\unbound_host_override_exists($af["host"], $af["domain"]) === true) { - $this->errors[] = APIResponse\get(2009); - } - } - // Nest our aliases under the item key - $aliases_fin = array("item" => $aliases_fin); - } - // Return our parsed aliases - return $aliases_fin; - } } \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundRestart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundRestart.inc new file mode 100644 index 000000000..ef2919d16 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundRestart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_restart("unbound", []); + return APIResponse\get(0, ["unbound" => "restarted"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundStart.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundStart.inc new file mode 100644 index 000000000..ab6eac212 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundStart.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_start("unbound", []); + return APIResponse\get(0, ["unbound" => "started"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundStop.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundStop.inc new file mode 100644 index 000000000..129df1d68 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APIServicesUnboundStop.inc @@ -0,0 +1,17 @@ +methods = ["POST"]; + $this->privileges = ["page-all", "page-status-services"]; + } + + public function action() { + service_control_stop("unbound", []); + return APIResponse\get(0, ["unbound" => "stopped"]); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemAPIErrors.inc b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemAPIErrors.inc new file mode 100644 index 000000000..b3f9f83f5 --- /dev/null +++ b/pfSense-pkg-API/files/etc/inc/api/api_models/APISystemAPIErrors.inc @@ -0,0 +1,17 @@ +methods = ["GET"]; + $this->requires_auth = false; + } + + public function action() { + return APIResponse\get(0, APIResponse\get(0, null, true)); + } +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc index b4bdfe6fb..a445c2769 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APIResponse.inc @@ -2,7 +2,7 @@ namespace APIResponse; # Pulls a assoc array API response from our response library. Optionally formats descriptive data into messages. -function get($id, $data=[]) { +function get($id, $data=[], $all=false) { $responses = [ // 0-999 reserved for system API level responses 0 => [ @@ -223,6 +223,345 @@ function get($id, $data=[]) { "return" => $id, "message" => "Host override deletion criteria not met" ], + + // 3000-3999 reserved for /interfaces API calls + 3000 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Unknown interface ID" + ], + 3001 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface ID in use" + ], + 3002 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface ID required" + ], + 3003 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface MAC address" + ], + 3004 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface MTU value" + ], + 3005 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface MSS value" + ], + 3006 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface MSS value greater than parent interface" + ], + 3007 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface speed and duplex" + ], + 3008 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface descriptive name already in use" + ], + 3009 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface IPv4 address already in use" + ], + 3010 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv4 address" + ], + 3011 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface type staticv4 requires IPv4 address" + ], + 3012 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv4 subnet" + ], + 3013 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface type staticv4 requires subnet bitmask" + ], + 3014 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv4 gateway" + ], + 3015 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP alias address" + ], + 3016 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP reject from address" + ], + 3017 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP timeout value" + ], + 3018 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP retry value" + ], + 3019 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP timeout value" + ], + 3020 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP reboot value" + ], + 3021 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP backoff cutoff value" + ], + 3022 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP initial interval value" + ], + 3023 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP configuration file" + ], + 3024 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP VLAN priority value" + ], + 3025 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv4 configuration type" + ], + 3026 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv6 address" + ], + 3027 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface IPv6 address already in use" + ], + 3028 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface staticv6 requires IPv6 address" + ], + 3029 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv6 subnet" + ], + 3030 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface type staticv6 requires subnet bitmask" + ], + 3031 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv6 gateway" + ], + 3032 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv6 prefix delegation size" + ], + 3033 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP6 VLAN priority value" + ], + 3034 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface DHCP6 configuration file" + ], + 3035 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface 6RD gateway address" + ], + 3036 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface 6RD gateway address required" + ], + 3037 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface 6RD prefix length" + ], + 3038 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv6 track interface" + ], + 3039 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface IPv6 track interface required" + ], + 3040 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv6 track prefix hex value" + ], + 3041 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface IPv6 configuration type" + ], + 3042 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface WAN cannot be deleted" + ], + 3043 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface cannot be deleted while member of interface group" + ], + 3044 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface cannot be deleted while member of bridge interface" + ], + 3045 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface cannot be deleted while member of GRE tunnel" + ], + 3046 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface cannot be deleted while member of GIF tunnel" + ], + 3047 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface cannot be deleted with existing traffic shaper configuration" + ], + 3048 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface VLAN tag required" + ], + 3049 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface VLAN tag in use" + ], + 3050 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface VLAN does not exist" + ], + 3051 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface VLAN parent does not exist" + ], + 3052 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface VLAN tag" + ], + 3053 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Invalid interface VLAN priority value" + ], + 3054 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface VLAN already exists on parent" + ], + 3055 => [ + "status" => "bad request", + "code" => 400, + "return" => $id, + "message" => "Interface VLAN parent required" + ], + // 4000-4999 reserved for /firewall API calls 4000 => [ "status" => "bad request", @@ -835,5 +1174,8 @@ function get($id, $data=[]) { $response = $responses[$id]; $response["data"] = $data; + if ($all === true) { + $response = $responses; + } return $response; } diff --git a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc index 63547b6c2..b83754919 100644 --- a/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc +++ b/pfSense-pkg-API/files/etc/inc/api/framework/APITools.inc @@ -701,3 +701,237 @@ function unbound_host_override_exists($hostname, $domain) { return $host_exists; } +// Get a complete config list of ALL interfaces. Based off interfaces_assign.php +function get_all_avail_interfaces() { + // Local variables + global $err_lib, $config; + $base_ifs = get_interface_list(); // Get our base interface list, this will be populated with all ifs + // Add wireless ifs to our array + if (is_array($config['wireless']['clone']) && count($config['wireless']['clone'])) { + foreach ($config['wireless']['clone'] as $clone) { + $base_ifs[$clone['cloneif']] = $clone; + $base_ifs[$clone['cloneif']]['is_wlclone'] = true; + } + } + // Add VLAN ifs to our array + if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { + //$timea = microtime(true); + foreach ($config['vlans']['vlan'] as $vlan) { + $base_ifs[$vlan['vlanif']] = $vlan; + $base_ifs[$vlan['vlanif']]['is_vlan'] = true; + } + } + // Add bridge ifs to our array + if (is_array($config['bridges']['bridged']) && count($config['bridges']['bridged'])) { + foreach ($config['bridges']['bridged'] as $bridge) { + $base_ifs[$bridge['bridgeif']] = $bridge; + $base_ifs[$bridge['bridgeif']]['is_bridge'] = true; + } + } + // Add GIF ifs to our array + if (is_array($config['gifs']['gif']) && count($config['gifs']['gif'])) { + foreach ($config['gifs']['gif'] as $gif) { + $base_ifs[$gif['gifif']] = $gif; + $base_ifs[$gif['gifif']]['is_gif'] = true; + } + } + // Add GRE ifs to our array + if (is_array($config['gres']['gre']) && count($config['gres']['gre'])) { + foreach ($config['gres']['gre'] as $gre) { + $base_ifs[$gre['greif']] = $gre; + $base_ifs[$gre['greif']]['is_gre'] = true; + } + } + // Add LAGG ifs to our array + if (is_array($config['laggs']['lagg']) && count($config['laggs']['lagg'])) { + foreach ($config['laggs']['lagg'] as $lagg) { + $base_ifs[$lagg['laggif']] = $lagg; + $base_ifs[$lagg['laggif']]['is_lagg'] = true; + /* LAGG members cannot be assigned */ + $lagifs = explode(',', $lagg['members']); + foreach ($lagifs as $lagif) { + if (isset($base_ifs[$lagif])) { + unset($base_ifs[$lagif]); + } + } + } + } + // Add QinQ ifs to our array + if (is_array($config['qinqs']['qinqentry']) && count($config['qinqs']['qinqentry'])) { + foreach ($config['qinqs']['qinqentry'] as $qinq) { + $base_ifs["{$qinq['vlanif']}"]['descr'] = "VLAN {$qinq['tag']} on {$qinq['if']}"; + $base_ifs["{$qinq['vlanif']}"]['is_qinq'] = true; + /* QinQ members */ + $qinqifs = explode(' ', $qinq['members']); + foreach ($qinqifs as $qinqif) { + $base_ifs["{$qinq['vlanif']}.{$qinqif}"]['descr'] = "QinQ {$qinqif} on VLAN {$qinq['tag']} on {$qinq['if']}"; + $base_ifs["{$qinq['vlanif']}.{$qinqif}"]['is_qinq'] = true; + } + } + } + // Add PPP ifs to our array + if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) { + foreach ($config['ppps']['ppp'] as $pppid => $ppp) { + $if_name = $ppp['if']; + $base_ifs[$if_name] = $ppp; + $base_ifs[$if_name]['is_ppp'] = true; + $ports_base = basename($ppp['ports']); + if (isset($ppp['descr'])) { + $base_ifs[$if_name]['descr'] = strtoupper($ppp['if']). "({$ports_base}) - {$ppp['descr']}"; + } else if (isset($ppp['username'])) { + $base_ifs[$if_name]['descr'] = strtoupper($ppp['if']). "({$ports_base}) - {$ppp['username']}"; + } else { + $base_ifs[$if_name]['descr'] = strtoupper($ppp['if']). "({$ports_base})"; + } + } + } + // Add OpenVPN descriptions to our array + $ovpn_descrs = array(); + if (is_array($config['openvpn'])) { + if (is_array($config['openvpn']['openvpn-server'])) { + foreach ($config['openvpn']['openvpn-server'] as $s) { + $if_name = "ovpns{$s['vpnid']}"; + $base_ifs[$if_name] = $s; + $ovpn_descrs[$s['vpnid']] = $s['description']; + } + } + if (is_array($config['openvpn']['openvpn-client'])) { + foreach ($config['openvpn']['openvpn-client'] as $c) { + $if_name = "ovpnc{$c['vpnid']}"; + $base_ifs[$if_name] = $c; + $ovpn_descrs[$c['vpnid']] = $c['description']; + } + } + } + // Add IPsec descriptions to our array + global $err_lib, $ipsec_descrs; + $ipsec_descrs = interface_ipsec_vti_list_all(); + foreach ($ipsec_descrs as $ifname => $ifdescr) { + $base_ifs[$ifname] = array('descr' => $ifdescr); + } + // Loop through our array and check if interface is in use + foreach ($base_ifs as $pid => $conf) { + $pf_id = get_pfsense_if_id($pid); // Try to convert to pfSense interface ID + // Check if our pfSense ID was found + if (get_pfsense_if_id($pid)) { + $base_ifs[$pid]["in_use"] = $pf_id; + } + } + return $base_ifs; +} + +// Get available media options for a given interface. Modified from interfaces.php +function get_if_media_options($interface, $physical_if=false) { + // Local variables + global $err_lib, $config; + $interface = ($physical_if !== true) ? get_pfsense_if_id($interface) : $interface; + $interface = ($physical_if !== true) ? $config['interfaces'][$interface]['if'] : $interface; + $mediaopts_list = array(); + exec("/sbin/ifconfig -m $interface | grep \"media \"", $mediaopts); + foreach ($mediaopts as $mediaopt) { + preg_match("/media (.*)/", $mediaopt, $matches); + if (preg_match("/(.*) mediaopt (.*)/", $matches[1], $matches1)) { + // there is media + mediaopt like "media 1000baseT mediaopt full-duplex" + array_push($mediaopts_list, $matches1[1] . " " . $matches1[2]); + } else { + // there is only media like "media 1000baseT" + array_push($mediaopts_list, $matches[1]); + } + } + return $mediaopts_list; +} + +// Get our next available pfSense interface ID +function get_next_pfsense_if_id() { + // Local variables + global $err_lib, $config; + $curr_ifs = $config["interfaces"]; + // Check if we have our `wan` or `lan` pf IDs in use, if so, find the next OPTx id + if (!array_key_exists("wan", $curr_ifs)) { + return "wan"; + } elseif (!array_key_exists("lan", $curr_ifs)) { + return "lan"; + } else { + // Loop until we find an unused OPTx interface + foreach (range(1, 2000) as $count) { + // Check if this OPTx ID exists + $optx = "opt".strval($count); + if (!array_key_exists($optx, $curr_ifs)) { + return $optx; + } + } + } +} + +// Returns a list of dynamically configured IPv6 interfaces. Modified from interfaces.php function. +function get_ipv6_if_list() { + global $err_lib, $config, $section; + $list = array('' => ''); + $interfaces = get_configured_interface_with_descr(true); + $dyn_v6_ifs = array(); + foreach ($interfaces as $iface => $ifacename) { + switch ($config['interfaces'][$iface]['ipaddrv6']) { + case "6to4": + case "6rd": + case "dhcp6": + $dyn_v6_ifs[$iface] = array( + 'name' => $ifacename, + 'ipv6_num_prefix_ids' => pow(2, (int) calculate_ipv6_delegation_length($iface)) - 1); + break; + default: + continue 2; + } + } + return($dyn_v6_ifs); +} + +// Apply a new interface configuration +function apply_interface_config($if_conf) { + // Local variables + global $err_lib, $config; + $vlan_redo = false; + // Check that our if configuration is an array + if (is_array($if_conf)) { + foreach ($if_conf as $if_apply => $if_go) { + if (isset($config['interfaces'][$if_apply]['enable'])) { + interface_bring_down($if_apply, false, $if_go); + interface_configure($if_apply, true); + if ($config['interfaces'][$if_apply]['ipaddrv6'] == "track6") { + // Configure IPv6 track6 type if present + $wan_cfg = $config['interfaces'][$if_apply]; + interface_track6_configure($if_apply, $wan_cfg, true); + } + } else { + interface_bring_down($if_apply, true, $if_go); + // Restart DHCPD if enabled + if (isset($config['dhcpd'][$if_apply]['enable']) || + isset($config['dhcpdv6'][$if_apply]['enable'])) { + services_dhcpd_configure(); + } + } + // Check if VLANs are configured for this interface, if so, reapply them + if (interface_has_clones(get_real_interface($if_apply))) { + $vlan_redo = true; + } + } + } + // Check if DHCP needs to be reloaded + if ($if_conf['ipaddr'] == "dhcp") { + kill_dhclient_process($if_conf['if']); + } + if ($if_conf['ipaddrv6'] == "dhcp6") { + kill_dhcp6client_process($if_conf['if'],true); + } + // Reapply VLANs if necessary + if ($vlan_redo) { + interfaces_vlan_configure(); + } + // Restart services that may reference IP + services_snmpd_configure(); + setup_gateways_monitor(); + filter_configure(); + enable_rrd_graphing(); + system_routing_configure(); + // Return true or false if configuration was successful + return true; +} \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/add/index.php index ed95a8977..3401795f9 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/add/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/delete/index.php index f6ca9ce8e..79a3594aa 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/index.php index d29d66f9b..6b4a1c996 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/add/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/add/index.php index b74427dde..1bb74cd8d 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/add/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/add/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/delete/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/delete/index.php index 7f87bfd8c..3bc3a49c5 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/delete/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/delete/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/index.php index 5991c3f62..702e8736b 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/modify/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/modify/index.php index d0f53205d..72aaf97b3 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/modify/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/interfaces/vlans/modify/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/add/hosts/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/add/hosts/index.php index ea9edb09c..23b64ef44 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/add/hosts/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/add/hosts/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/apply/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/apply/index.php index 8245d6352..659eda46b 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/apply/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/apply/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/restart/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/restart/index.php index ec9f18a46..4e6fff551 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/restart/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/restart/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/start/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/start/index.php index 806d7f2f9..3786cf5cd 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/start/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/start/index.php @@ -1,10 +1,4 @@ listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/stop/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/stop/index.php index 601fa3b43..d95766172 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/stop/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/services/unbound/stop/index.php @@ -1,10 +1,4 @@ -listen(); \ No newline at end of file diff --git a/pfSense-pkg-API/files/usr/local/www/api/v1/system/api/errors/index.php b/pfSense-pkg-API/files/usr/local/www/api/v1/system/api/errors/index.php index 0fa494d19..096c73aca 100644 --- a/pfSense-pkg-API/files/usr/local/www/api/v1/system/api/errors/index.php +++ b/pfSense-pkg-API/files/usr/local/www/api/v1/system/api/errors/index.php @@ -1,6 +1,3 @@ listen(); \ No newline at end of file From 5f962ba78ed0bc4e3ddaa4216e9e7a4859ea357e Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Wed, 26 Aug 2020 14:44:56 -0600 Subject: [PATCH 15/16] Finalized transition to object oriented structure, minor adjustments to framework, added docs, added base unit tests, added tools to build package --- .../docs => docs}/CONTRIBUTING.md | 28 +- pfSense-pkg-API/Makefile | 340 +- pfSense-pkg-API/docs/index.rst | 68 - pfSense-pkg-API/files/etc/inc/api.inc | 1348 ---- pfSense-pkg-API/files/etc/inc/apicalls.inc | 6866 ----------------- pfSense-pkg-API/files/etc/inc/apiresp.inc | 208 - pfSense-pkg-API/files/pkg-deinstall.in | 1 - pfSense-pkg-API/pkg-plist | 434 +- tests/README.md | 35 + tests/test_api_v1_access_token.py | 37 + tests/test_installed.py | 28 + tools/README.md | 26 + tools/make_package.py | 48 + tools/templates/Makefile.j2 | 28 + tools/templates/pkg-plist.j2 | 6 + 15 files changed, 792 insertions(+), 8709 deletions(-) rename {pfSense-pkg-API/docs => docs}/CONTRIBUTING.md (92%) delete mode 100644 pfSense-pkg-API/docs/index.rst delete mode 100644 pfSense-pkg-API/files/etc/inc/api.inc delete mode 100644 pfSense-pkg-API/files/etc/inc/apicalls.inc delete mode 100644 pfSense-pkg-API/files/etc/inc/apiresp.inc create mode 100644 tests/README.md create mode 100644 tests/test_api_v1_access_token.py create mode 100644 tests/test_installed.py create mode 100644 tools/README.md create mode 100644 tools/make_package.py create mode 100644 tools/templates/Makefile.j2 create mode 100644 tools/templates/pkg-plist.j2 diff --git a/pfSense-pkg-API/docs/CONTRIBUTING.md b/docs/CONTRIBUTING.md similarity index 92% rename from pfSense-pkg-API/docs/CONTRIBUTING.md rename to docs/CONTRIBUTING.md index 433894cd4..30676cf87 100644 --- a/pfSense-pkg-API/docs/CONTRIBUTING.md +++ b/docs/CONTRIBUTING.md @@ -29,7 +29,7 @@ changes will be made available upon the next release. Coding Conventions ------------------ Make an attempt to match the format of existing code. The basic conventions are: -- Comments are recommended as long as they are brief, meaningful, and short +- Comments are recommended as long as they are brief, meaningful, and non-intrusive - Variables should be defined using snake case (e.g. snake_case) - Functions should be defined using snake case (e.g. snake_case()) - Constants should be defined using upper snake case (e.g. SNAKE_CASE) @@ -45,7 +45,7 @@ a few different components. It is strongly recommended that you familiarize your diving into creating endpoints. To get started writing your own endpoints, please follow the steps below: ### Things to Know ### - - The API is based on REST principals. Unfortunately, pfSense does not allow any custom changes to the NGINX + - The API is based on REST principals. Unfortunately, pfSense does not seem to allow any custom changes to the NGINX configuration so alternate request methods like `PUT` and `DELETE` do not appear to be possible. To accommodate this, the requested action must be defined in the endpoint path. - Create actions must be a `POST` request to an endpoint ending in `/add/` @@ -59,7 +59,7 @@ diving into creating endpoints. To get started writing your own endpoints, pleas ### Writing the API model ### At the core of the API endpoint is the API model. This is a class that validates client request data, writes changes to the pfSense configuration file, and makes any corresponding system changes. pfSense API is distributed with a -custom microframework to accommodate developers wanting to contribute or create their own API models and endpoints. +custom micro-framework to accommodate developers wanting to contribute or create their own API models and endpoints. #### Getting Started #### To get started creating a new API model, you first need to create a new PHP file in `/files/etc/inc/api/api_models` and @@ -76,7 +76,7 @@ class NewAPIModel extends APIBaseModel { #### Constructing the API Model #### In order to use the APIBaseModel framework, you must add a `__construct()` method to your new API model class and -initialize the APIBaseModel class as such. Additionally, you will specify your model attribute overrides within this +initialize the APIBaseModel class as such. Additionally, you may specify any model attribute overrides within this method: ```php @@ -111,7 +111,7 @@ privileges can be found here: https://github.com/pfsense/pfsense/blob/master/src - `$this->requires_auth` : Specify whether authentication and authorization is required for the API model. If set to `false` clients will not have to authenticate or have privilege to access. Defaults to `true`. -- `$this->set_auth_mode` : Allows you to statically specify the API authentication mode. For example, if you are +- `$this->set_auth_mode` : Allows you to explicitly specify the API authentication mode. For example, if you are writing a model that tests user's local database credentials and do not want the model to assume the API's configured auth mode you would specify `$this->set_auth_mode = "local";` to always force local authentication. Defaults to the API's configured auth mode in the /api/ webConfigurator page. @@ -286,21 +286,9 @@ Often times you will need to create functions to condense redundant tasks. You c `$some_variable = APITools\your_custom_tool_function();` ### Adding Models and Endpoints to the Package -After you have written your API models and endpoint and have tested it's functionality, you must specify your endpoint -files in the package makefile. Otherwise, it will not be included in the package in the next release. - -1. Add the following lines to the `Makefile` located in this repo. **Be sure to change the file paths to match the files -you have created**: -``` -${MKDIR} ${STAGEDIR}${PREFIX}/www/api/v1/system/test -${INSTALL_DATA} ${FILESDIR}${PREFIX}/www/api/v1/system/test/index.php \ - ${STAGEDIR}${PREFIX}/www/api/v1/system/test -``` -2. Add the following lines to the `pkg-plist` file located in this repo. Be sure to change the file paths to match the -files you have created: - - For each directory created, add: `@dir /usr/local/www/api/v1/status/carp/modify` - - For each index.php endpoint created, add `/usr/local/www/api/v1/status/carp/modify/index.php` - - For each API model file created, add `/etc/inc/api/api_models/YourAPIModel.inc` +The package Makefile and pkg-plist files are auto generated by `tools/make_package.py`. This simply pulls the files and +directories needed to build our package from the `files` directory. For more information on building the package, refer +to `tools/README.md` Questions diff --git a/pfSense-pkg-API/Makefile b/pfSense-pkg-API/Makefile index 7e3726cdf..b6c82d06c 100644 --- a/pfSense-pkg-API/Makefile +++ b/pfSense-pkg-API/Makefile @@ -1,25 +1,329 @@ # $FreeBSD$ -PORTNAME= pfSense-pkg-API -PORTVERSION= 0.0 -PORTREVISION= 3 -CATEGORIES= sysutils -MASTER_SITES= # empty -DISTFILES= # empty -EXTRACT_ONLY= # empty +PORTNAME=pfSense-pkg-API +PORTVERSION=0.0 +PORTREVISION=3 +CATEGORIES=sysutils +MASTER_SITES=# empty +DISTFILES=# empty +EXTRACT_ONLY=# empty MAINTAINER= jaredhendrickson13@gmail.com -COMMENT= pfSense API package -LICENSE= APACHE20 -NO_BUILD= yes -NO_MTREE= yes -SUB_FILES= pkg-install pkg-deinstall -SUB_LIST= PORTNAME=${PORTNAME} +COMMENT=pfSense API package +LICENSE=APACHE20 +NO_BUILD=yes +NO_MTREE=yes +SUB_FILES=pkg-install pkg-deinstall +SUB_LIST=PORTNAME=${PORTNAME} do-extract: - ${MKDIR} ${WRKSRC} - -do-intall: - @echo "YES" - + ${MKDIR} ${WRKSRC} +do-install: + ${MKDIR} ${STAGEDIR}/usr/local + ${MKDIR} ${STAGEDIR}/usr/local/www + ${MKDIR} ${STAGEDIR}/usr/local/pkg + ${MKDIR} ${STAGEDIR}/usr/local/share + ${MKDIR} ${STAGEDIR}/usr/local/www/api + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1 + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/status + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/routing + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/access_token + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/interfaces + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/status/carp + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/status/carp/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/nat + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/virtualips + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/states + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/rules + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/nat/portforwards + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/nat/portforwards/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/nat/portforwards/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/virtualips/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/virtualips/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/states/size + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/states/size/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/rules/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/rules/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/delete/address + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/add/address + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/config + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/certificates + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/version + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/api + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/hostname + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/arp + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/dns + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/certificates/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/certificates/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/api/errors + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/hostname/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/arp/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/dns/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/dns/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/system/dns/delete/servers + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/authservers + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/delete/privs + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/delete/groups + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/authservers/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/authservers/ldap + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/authservers/radius + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/authservers/ldap/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/authservers/ldap/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/authservers/radius/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/add/privs + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/users/add/groups + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/routing/gateways + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/ntpd + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/sshd + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/syslogd + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/start + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/stop + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/restart + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/dhcpd + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/dpinger + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/ntpd/start + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/ntpd/stop + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/ntpd/restart + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/sshd/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/sshd/start + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/sshd/stop + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/sshd/restart + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/syslogd/start + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/syslogd/stop + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/syslogd/restart + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/dhcpd/start + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/dhcpd/stop + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/dhcpd/restart + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/dpinger/start + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/dpinger/stop + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/dpinger/restart + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/apply + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/start + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/stop + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/restart + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/delete/hosts + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/modify/hosts + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/services/unbound/add/hosts + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/interfaces/vlans + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/interfaces/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/interfaces/add + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/interfaces/vlans/delete + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/interfaces/vlans/modify + ${MKDIR} ${STAGEDIR}/usr/local/www/api/v1/interfaces/vlans/add + ${MKDIR} ${STAGEDIR}/usr/local/share/pfSense-pkg-API + ${MKDIR} ${STAGEDIR}/etc/inc + ${MKDIR} ${STAGEDIR}/etc/inc/php-jwt + ${MKDIR} ${STAGEDIR}/etc/inc/api + ${MKDIR} ${STAGEDIR}/etc/inc/php-jwt/src + ${MKDIR} ${STAGEDIR}/etc/inc/api/framework + ${MKDIR} ${STAGEDIR}/etc/inc/api/api_models + + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/index.php ${STAGEDIR}/usr/local/www/api + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/api_return_codes.txt ${STAGEDIR}/usr/local/www/api/v1 + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/status/carp/index.php ${STAGEDIR}/usr/local/www/api/v1/status/carp + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/status/carp/modify/index.php ${STAGEDIR}/usr/local/www/api/v1/status/carp/modify + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/nat/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/nat + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/nat/portforwards/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/nat/portforwards + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/nat/portforwards/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/nat/portforwards/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/nat/portforwards/add/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/nat/portforwards/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/virtualips/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/virtualips + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/virtualips/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/virtualips/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/virtualips/add/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/virtualips/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/states/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/states + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/states/size/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/states/size + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/states/size/modify/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/states/size/modify + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/rules/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/rules + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/rules/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/rules/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/rules/add/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/rules/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/aliases/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/aliases/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/aliases/delete/address/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/delete/address + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/aliases/modify/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/modify + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/aliases/add/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/firewall/aliases/add/address/index.php ${STAGEDIR}/usr/local/www/api/v1/firewall/aliases/add/address + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/config/index.php ${STAGEDIR}/usr/local/www/api/v1/system/config + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/certificates/index.php ${STAGEDIR}/usr/local/www/api/v1/system/certificates + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/certificates/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/system/certificates/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/certificates/add/index.php ${STAGEDIR}/usr/local/www/api/v1/system/certificates/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/version/index.php ${STAGEDIR}/usr/local/www/api/v1/system/version + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/api/index.php ${STAGEDIR}/usr/local/www/api/v1/system/api + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/api/errors/index.php ${STAGEDIR}/usr/local/www/api/v1/system/api/errors + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/hostname/index.php ${STAGEDIR}/usr/local/www/api/v1/system/hostname + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/hostname/modify/index.php ${STAGEDIR}/usr/local/www/api/v1/system/hostname/modify + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/arp/index.php ${STAGEDIR}/usr/local/www/api/v1/system/arp + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/arp/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/system/arp/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/dns/index.php ${STAGEDIR}/usr/local/www/api/v1/system/dns + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/dns/delete/servers/index.php ${STAGEDIR}/usr/local/www/api/v1/system/dns/delete/servers + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/system/dns/modify/index.php ${STAGEDIR}/usr/local/www/api/v1/system/dns/modify + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/index.php ${STAGEDIR}/usr/local/www/api/v1/users + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/users/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/delete/privs/index.php ${STAGEDIR}/usr/local/www/api/v1/users/delete/privs + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/delete/groups/index.php ${STAGEDIR}/usr/local/www/api/v1/users/delete/groups + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/modify/index.php ${STAGEDIR}/usr/local/www/api/v1/users/modify + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/authservers/index.php ${STAGEDIR}/usr/local/www/api/v1/users/authservers + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/authservers/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/users/authservers/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/authservers/ldap/index.php ${STAGEDIR}/usr/local/www/api/v1/users/authservers/ldap + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/authservers/ldap/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/users/authservers/ldap/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/authservers/ldap/add/index.php ${STAGEDIR}/usr/local/www/api/v1/users/authservers/ldap/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/authservers/radius/index.php ${STAGEDIR}/usr/local/www/api/v1/users/authservers/radius + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/authservers/radius/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/users/authservers/radius/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/add/index.php ${STAGEDIR}/usr/local/www/api/v1/users/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/add/privs/index.php ${STAGEDIR}/usr/local/www/api/v1/users/add/privs + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/users/add/groups/index.php ${STAGEDIR}/usr/local/www/api/v1/users/add/groups + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/routing/gateways/index.php ${STAGEDIR}/usr/local/www/api/v1/routing/gateways + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/access_token/index.php ${STAGEDIR}/usr/local/www/api/v1/access_token + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/index.php ${STAGEDIR}/usr/local/www/api/v1/services + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/ntpd/start/index.php ${STAGEDIR}/usr/local/www/api/v1/services/ntpd/start + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/ntpd/stop/index.php ${STAGEDIR}/usr/local/www/api/v1/services/ntpd/stop + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/ntpd/restart/index.php ${STAGEDIR}/usr/local/www/api/v1/services/ntpd/restart + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/sshd/index.php ${STAGEDIR}/usr/local/www/api/v1/services/sshd + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/sshd/modify/index.php ${STAGEDIR}/usr/local/www/api/v1/services/sshd/modify + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/sshd/start/index.php ${STAGEDIR}/usr/local/www/api/v1/services/sshd/start + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/sshd/stop/index.php ${STAGEDIR}/usr/local/www/api/v1/services/sshd/stop + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/sshd/restart/index.php ${STAGEDIR}/usr/local/www/api/v1/services/sshd/restart + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/syslogd/start/index.php ${STAGEDIR}/usr/local/www/api/v1/services/syslogd/start + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/syslogd/stop/index.php ${STAGEDIR}/usr/local/www/api/v1/services/syslogd/stop + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/syslogd/restart/index.php ${STAGEDIR}/usr/local/www/api/v1/services/syslogd/restart + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/start/index.php ${STAGEDIR}/usr/local/www/api/v1/services/start + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/stop/index.php ${STAGEDIR}/usr/local/www/api/v1/services/stop + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/restart/index.php ${STAGEDIR}/usr/local/www/api/v1/services/restart + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/dhcpd/start/index.php ${STAGEDIR}/usr/local/www/api/v1/services/dhcpd/start + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/dhcpd/stop/index.php ${STAGEDIR}/usr/local/www/api/v1/services/dhcpd/stop + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/dhcpd/restart/index.php ${STAGEDIR}/usr/local/www/api/v1/services/dhcpd/restart + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/dpinger/start/index.php ${STAGEDIR}/usr/local/www/api/v1/services/dpinger/start + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/dpinger/stop/index.php ${STAGEDIR}/usr/local/www/api/v1/services/dpinger/stop + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/dpinger/restart/index.php ${STAGEDIR}/usr/local/www/api/v1/services/dpinger/restart + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/unbound/index.php ${STAGEDIR}/usr/local/www/api/v1/services/unbound + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/unbound/apply/index.php ${STAGEDIR}/usr/local/www/api/v1/services/unbound/apply + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/unbound/delete/hosts/index.php ${STAGEDIR}/usr/local/www/api/v1/services/unbound/delete/hosts + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/unbound/modify/hosts/index.php ${STAGEDIR}/usr/local/www/api/v1/services/unbound/modify/hosts + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/unbound/start/index.php ${STAGEDIR}/usr/local/www/api/v1/services/unbound/start + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/unbound/stop/index.php ${STAGEDIR}/usr/local/www/api/v1/services/unbound/stop + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/unbound/restart/index.php ${STAGEDIR}/usr/local/www/api/v1/services/unbound/restart + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/services/unbound/add/hosts/index.php ${STAGEDIR}/usr/local/www/api/v1/services/unbound/add/hosts + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/interfaces/index.php ${STAGEDIR}/usr/local/www/api/v1/interfaces + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/interfaces/vlans/index.php ${STAGEDIR}/usr/local/www/api/v1/interfaces/vlans + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/interfaces/vlans/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/interfaces/vlans/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/interfaces/vlans/modify/index.php ${STAGEDIR}/usr/local/www/api/v1/interfaces/vlans/modify + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/interfaces/vlans/add/index.php ${STAGEDIR}/usr/local/www/api/v1/interfaces/vlans/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/interfaces/delete/index.php ${STAGEDIR}/usr/local/www/api/v1/interfaces/delete + ${INSTALL_DATA} ${FILESDIR}/usr/local/www/api/v1/interfaces/add/index.php ${STAGEDIR}/usr/local/www/api/v1/interfaces/add + ${INSTALL_DATA} ${FILESDIR}/usr/local/pkg/api.xml ${STAGEDIR}/usr/local/pkg + ${INSTALL_DATA} ${FILESDIR}/usr/local/share/pfSense-pkg-API/info.xml ${STAGEDIR}/usr/local/share/pfSense-pkg-API + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/LICENSE ${STAGEDIR}/etc/inc/php-jwt + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/README.md ${STAGEDIR}/etc/inc/php-jwt + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/composer.json ${STAGEDIR}/etc/inc/php-jwt + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/SignatureInvalidException.php ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/BeforeValidException.php ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/JWK.php ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/ExpiredException.php ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/php-jwt/src/JWT.php ${STAGEDIR}/etc/inc/php-jwt/src + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/framework/APITools.inc ${STAGEDIR}/etc/inc/api/framework + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/framework/APIAuth.inc ${STAGEDIR}/etc/inc/api/framework + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/framework/APIBaseModel.inc ${STAGEDIR}/etc/inc/api/framework + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/framework/APIResponse.inc ${STAGEDIR}/etc/inc/api/framework + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAuthservers.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesUnboundRestart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAuthserversLDAPDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallAliasesAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIStatusCarpModify.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallVirtualIPs.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIInterfacesAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesUnbound.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesSyslogdRestart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemDNSModify.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesRestart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesSSHdModify.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesUnboundStop.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesSSHdRestart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesNtpdStart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIInterfacesVLANs.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesSSHdStop.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIInterfacesVLANsModify.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIAccessToken.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesUnboundDeleteHosts.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemARP.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemCertificates.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesDpingerStop.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallRulesAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemARPDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesSyslogdStop.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAuthserversLDAPAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIInterfacesDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesSyslogdStart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersModify.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAuthserversRADIUS.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemConfig.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemAPI.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallAliasesModify.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesNtpdRestart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesUnboundStart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemHostnameModify.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesDHCPdStart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAddPrivs.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesStart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallNat.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersDeletePrivs.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallAliases.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemDNS.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIInterfaces.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallStates.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesDpingerStart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAuthserversDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersDeleteGroups.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemCertificatesAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesSSHdStart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemVersion.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallAliasesDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIInterfacesVLANsAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallStatesSizeModify.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemDNSDeleteServers.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallNatPortForwards.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIRoutingGateways.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIStatusCarp.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAuthserversLDAP.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallStatesSize.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemCertificatesDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemHostname.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesDpingerRestart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallAliasesAddAddress.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallAliasesDeleteAddress.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAuthserversRADIUSDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesUnboundApply.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesUnboundAddHosts.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesNtpdStop.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesDHCPdStop.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APISystemAPIErrors.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIInterfacesVLANsDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallRulesDelete.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesSSHd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesDHCPdRestart.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsers.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIFirewallRules.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServices.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIServicesStop.inc ${STAGEDIR}/etc/inc/api/api_models + ${INSTALL_DATA} ${FILESDIR}/etc/inc/api/api_models/APIUsersAddGroups.inc ${STAGEDIR}/etc/inc/api/api_models + .include \ No newline at end of file diff --git a/pfSense-pkg-API/docs/index.rst b/pfSense-pkg-API/docs/index.rst deleted file mode 100644 index 16fb3e709..000000000 --- a/pfSense-pkg-API/docs/index.rst +++ /dev/null @@ -1,68 +0,0 @@ ---- -# Introduction -pfSense API is a fast, safe, full-fledged HTTP API. This works by leveraging the same PHP functions and processes used -by pfSense's webConfigurator into API endpoints to create, read, update and delete pfSense configurations. All API -endpoints enforce input validation to prevent invalid configurations from being made. Configurations made via API are -properly written to the master XML configuration and the correct backend configurations are made preventing the need for - a reboot. All this results in the fastest, safest, and easiest way to automate pfSense! - -# Installation -To install pfSense API, simply run the following command from the pfSense shell:
-`pkg add https://github.com/jaredhendrickson13/pfsense-api/releases/download/v0.0.3/pfSense-2-4-pkg-API-0.0_3.txz`
- -To uninstall, run the following command:
-`pkg delete pfSense-pkg-API`
- -_Note: if you do not have shell access to pfSense, you can still install via the webConfigurator by navigating to -'Diagnostics > Command Prompt' and enter the commands there_ - -# Requirements -- pfSense 2.4.4 or later is supported -- pfSense API requires a local user account in pfSense. The same permissions required to make configurations in the -webConfigurator are required to make calls to the API endpoints -- While not an enforced requirement, it is STRONGLY recommended that you configure pfSense to use HTTPS instead of HTTP. - This ensures that login credentials and/or API tokens remain secure in-transit - -# Authentication -By default, pfSense API uses the same credentials as the webConfigurator. This behavior allows you to configure pfSense -from the API out of the box, and user passwords may be changed from the API to immediately add additional security if -needed. After installation, you can navigate to System > API in the pfSense webConfigurator to configure API -authentication. - -To authenticate your API call, follow the instructions for your configured authentication mode: - -- Local Database (default) : Uses the same credentials as the pfSense webConfigurator. To authenticate API calls, simply -add a `client-id` value containing your username and a `client-token` value containing your password to your payload. For -example `{"client-id": "admin", "client-token": "pfsense"}` - -- JWT : Requires a bearer token to be included in the `Authorization` header of your request. To receive a bearer token, -you may make a POST request to /api/v1/access_token/ and include a `client-id` value containing your pfSense username -and a `client-token` value containing your pfSense password to your payload. For example -`{"client-id": "admin", "client-token": "pfsense"}`. Once you have your bearer token, you can authenticate your API -call by adding it to the request's authorization header. (e.g. `Authorization: Bearer xxxxxxxx.xxxxxxxxx.xxxxxxxx`) - -- API Token : Uses standalone tokens generated via the UI. These are better suited to distribute to systems as they are -revocable and will only allow API authentication and not UI or SSH authentication (like the local database credentials). -To generate or revoke credentials, navigate to System > API within the UI and ensure the Authentication Mode is set to -API token. Then you should have the options to configure API Token generation, generate new tokens, and revoke existing -tokens. Once you have your API token, you may authenticate your API call by adding a `client-id` value containing your -API token client ID and a `client-token` value containing your API token client token to your payload. -(e.g. `{"client-id": "cccdj-311s", "client-token": "42jkjl-k234jlk1b38123kj3kjl-ffwzzuilaei"}` - -# Response Codes -`200 (OK)` : API call succeeded
-`400 (Bad Request)` : An error was found within your requested parameters
-`401 (Unauthorized)` : API client has not completed authentication or authorization successfully
-`403 (Forbidden)` : The API endpoint has refused your call. Commonly due to your access settings found in `System > API`
-`404 (Not found)` : Either the API endpoint or requested data was not found
-`500 (Server error)` : The API endpoint encountered an unexpected error processing your API request
- -# Error Codes -A full list of error codes can be found by navigating to /api/v1/system/api/errors/ after installation. This will return - JSON data containing each error code and their corresponding error message. No authentication is required to view the - error code library. This also makes API integration with third-party software easy as the API error codes and messages - are always just an HTTP call away! - -# Rate limit -There is no limit to API calls at this time. Alternatively, you enable the API in read-only mode to only allow endpoints -with read access within System > API. diff --git a/pfSense-pkg-API/files/etc/inc/api.inc b/pfSense-pkg-API/files/etc/inc/api.inc deleted file mode 100644 index fe8056bbb..000000000 --- a/pfSense-pkg-API/files/etc/inc/api.inc +++ /dev/null @@ -1,1348 +0,0 @@ - "unauthorized", "code" => 401, "return" => 3, "message" => $err_lib[3]); // JSON array - -// Check if our URL encoded parameters are empty, if so try JSON encoded parameters -if (empty($client_params)) { - $client_params = json_decode(file_get_contents('php://input'), true); // Accept HTTP requests in JSON format -} - -// Check for our client-id and client-token -if (isset($client_params['client-id'])) { - $client_id = $client_params['client-id']; -} -if (isset($client_params['client-token'])) { - $client_token = $client_params['client-token']; -} - -// FUNCTIONS------------------------------------------------------------------------------------------------------------ -// Strip special characters and replace whitespace with underscore -function sanitize_str($string) { - $string = str_replace(' ', '_', $string); // Replace whitespace with underscore - $string = preg_replace('/[^A-Za-z0-9\-_.]/', '', $string); // Remove special characters - return $string; -} - -// Checks if a given string starts with another given string -function str_starts_with($needle, $haystack) { - $length = strlen($needle); - return (substr($haystack, 0, $length) === $needle); -} - -// Restarts the pfSense webConfigurator -function restart_webconfigurator() { - ob_flush(); - flush(); - log_error(gettext("webConfigurator configuration has changed. Restarting webConfigurator.")); - send_event("service restart webgui"); -} - -// API authentication debug function -function api_auth_debug($req_privs=array()) { - global $err_lib, $client_id, $client_params, $read_priv; - // Before doing anything, check that debug mode is set - if (array_key_exists("debug", $client_params)) { - // Local variables - $client_config =& getUserEntry($client_id);; - $client_privs = get_user_privileges($client_config); - $read_only_priv = true; - if (array_diff($read_priv, $client_privs)) { - $read_only_priv = false; - } - // Print our debug data - echo "API CLIENT:" . PHP_EOL; - echo var_dump($client_id) . PHP_EOL; - echo "API AUTHENTICATED:" . PHP_EOL; - echo var_dump(api_authenticate()) . PHP_EOL; - echo "API CLIENT PRIVILEGES:" . PHP_EOL; - echo var_dump($client_privs) . PHP_EOL; - echo "REQUIRED PRIVILEGES:" . PHP_EOL; - echo var_dump($req_privs) . PHP_EOL; - echo "READ ONLY:" . PHP_EOL; - echo var_dump($read_only_priv) . PHP_EOL; - } -} - -// Generate new API tokens for token auth mode -function api_generate_token($username) { - // Local variables - global $err_lib, $config; - $pkg_index = get_api_configuration()[0]; // Save our current API configs pkg index - $api_config = get_api_configuration()[1]; // Save our current API config - $key_hash_algo = $api_config["keyhash"]; // Pull our configured key hash algorithm - $key_bit_strength = $api_config["keybytes"]; // Pull our configured key bit strength - $key_user = bin2hex($username); // Save our user's dedicated API client-ID - $key_new = bin2hex(random_bytes(intval($key_bit_strength))); // Generate a new key - $key_hash = hash($key_hash_algo, $key_new); // Hash our key using our configured algos - // Loop through our existing keys to see - $api_config["keys"] = !is_array($api_config["keys"]) ? array("key" => []) : $api_config["keys"]; - $api_config["keys"]["key"][] = array("client_id" => $key_user, "client_token" => $key_hash, "algo" => $key_hash_algo); - // Write our changes - $config["installedpackages"]["package"][$pkg_index]["conf"] = $api_config; // Write change to config - $change_note = " Generated API key"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - return $key_new; -} - -// Creates JWT server key if one does not exist, or optionally allows rotation of the JWT server key -function api_create_jwt_server_key($rotate=false) { - global $config; - $pkg_index = get_api_configuration()[0]; // Save our current API configs pkg index - $api_config = get_api_configuration()[1]; // Save our current API config - # Create a new server key if one is not set - if (empty($api_config["server_key"]) or $rotate === true) { - $config["installedpackages"]["package"][$pkg_index]["conf"]["server_key"] = bin2hex(random_bytes(32)); - write_config(); - } -} - -// Creates a JWT to use for JWT authentication -function api_create_jwt($data) { - global $config; - $api_config = get_api_configuration()[1]; // Save our current API config - $token_exp = $api_config["jwt_exp"]; // Expire token in one hours - api_create_jwt_server_key(); // Ensure we have a JWT server key - $payload = array( - "iss" => $config["system"]["hostname"], - "exp" => time() + $token_exp, - "nbf" => time(), - "data" => $data - ); - return JWT::encode($payload, $api_config["server_key"]); -} - -// Decodes a JWT -function api_decode_jwt($token) { - $key = get_api_configuration()[1]["server_key"]; // Save our current server key - try { - $decoded = (array) JWT::decode($token, $key, array('HS256')); - } catch (Exception $e) { - $decoded = false; - } - return $decoded; -} - -// Get our API token ID for a given username -function api_get_existing_tokens($username) { - // Local variables - $api_config = get_api_configuration()[1]; - $key_user = bin2hex($username); // Save our user's dedicated API client-ID - $user_keys = []; - foreach ($api_config["keys"]["key"] as $id => $key) { - if ($key["client_id"] === $key_user) { - $user_keys[$id] = array("client_token" => $key["client_token"], "algo" => $key["algo"]); - } - } - return $user_keys; -} - -// Authenticate using an API token -function api_authenticate_token($cid, $ctoken) { - $authenticated = false; - $hex_to_user = pack("H*", $cid); - // First check if our hex decoded user exists - if (in_array($hex_to_user, index_users())) { - // Loop through each of our users API keys and check if key matches - foreach (api_get_existing_tokens($hex_to_user) as $id => $data) { - $hash_input_key = hash($data["algo"], $ctoken); // Hash our key using our configured algos - if ($hash_input_key === $data["client_token"]) { - $authenticated = true; - break; - } - } - } - return $authenticated; -} - -// Check if user authenticates successfully -function api_authenticate() { - // Local variables - global $err_lib, $config, $client_id, $client_token; - $authenticated = false; - $api_config = get_api_configuration()[1]; - $users = index_users(); - - // Check for JWT in Authorization header if authmode is set to JWT - if ($api_config["authmode"] === "jwt") { - $auth_header = explode(" ", $_SERVER["HTTP_AUTHORIZATION"]); - $token_type = $auth_header[0]; - $token = $auth_header[1]; - $decoded_jwt = api_decode_jwt($token); - - // Check that our JWT from our Authorization header is valid - if ($token_type === "Bearer" and $decoded_jwt !== false) { - unset($_SESSION["Username"]); - $client_id = $decoded_jwt["data"]; - $_SESSION["Username"] = $client_id; - $authenticated = true; - } - } - - // Format our client ID and token based on our configured auth mode - if ($api_config["authmode"] === "base64") { - $client_id = base64_decode($client_id); - $client_token = base64_decode($client_token); - } - // Check if client ID and token are valid, and if user has necessary privileges - $client_id = trim($client_id); - $client_token = trim($client_token); - if ($api_config["authmode"] === "token") { - if (api_authenticate_token($client_id, $client_token) === true) { - $client_id = pack("H*", $client_id); - unset($_SESSION["Username"]); - $_SESSION["Username"] = $client_id; - $authenticated = true; - } - } else { - // Check local auth - $local_auth = authenticate_user($client_id, $client_token); // Test auth locally - if ($local_auth === true) { - unset($_SESSION["Username"]); - $_SESSION["Username"] = $client_id; - $authenticated = true; - } - } - // Check if user is disabled - if (array_key_exists("disabled", $config["system"]["user"][$users[$client_id]])) { - $authenticated = false; - } - return $authenticated; -} - -// Check if user is authorized to run API function -function api_authorized($req_privs, $read_only=false) { - // Local variables - global $err_lib, $read_priv, $client_id, $api_resp; - $authorized = false; - api_auth_debug($req_privs); - // Check that we can successfully authenticate - if (api_authenticate() === true) { - $client_config =& getUserEntry($client_id);; - $client_privs = get_user_privileges($client_config); - // Check if API is in read-only mode - if (is_api_read_only() === $read_only or $read_only) { - // Loop through each of our req privs and ensure the client has them, also check if access is read only - foreach ($req_privs as &$priv) { - // Check if action is not read only - if ($read_only === false) { - if (array_diff($read_priv, $client_privs) and in_array($priv, $client_privs, true)) { - $authorized = true; - break; - } - } else { - if (in_array($priv, $client_privs)) { - $authorized = true; - break; - } - } - } - } - } else { - http_response_code(401); - echo json_encode($api_resp) . PHP_EOL; - die(); - } - // If authorization failed, return forbidden - if ($authorized !== true) { - http_response_code(401); - $api_resp["return"] = 4; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - echo json_encode($api_resp) . PHP_EOL; - die(); - } - return $authorized; -} - -// Run our pre-runtime checks -function api_runtime_allowed() { - api_version_check(); // Die if incompatible pfSense version - api_enabled(); // Die if API is disabled in configuration - api_whitelist_check(); // Die if server address is not whitelisted -} - -// Read our API configuration -function get_api_configuration() { - global $config; - $api_pkg_name = "API"; - $pkg_conf = $config["installedpackages"]["package"]; - // Check that our configuration is an array - if (is_array($pkg_conf)) { - // Loop through our packages and find our API package config - foreach ($pkg_conf as $id => $pkg) { - if ($pkg["name"] === $api_pkg_name) { - return array($id, $pkg["conf"]); - } - } - } -} - -// Check if the API is enabled before answering calls, if not, redirect to wc login -function api_enabled() { - $api_config = get_api_configuration()[1]; - if (!isset($api_config["enable"])) { - header("Location: /"); - die(); - } -} - -// Check if the API is in read-only mode -function is_api_read_only() { - // Local variables - $api_config = get_api_configuration()[1]; // Save our current API config - if (array_key_exists("readonly", $api_config)) { - return true; - } else { - return false; - } -} - -// Check if our pfSense version is allowed to run this version of API -function api_version_check() { - # Local variables - global $err_lib; - $curr_ver = str_replace(".", "", explode("-", get_pfsense_version()["version"])[0]); - $min_ver = 244; - $curr_ver = is_numeric($curr_ver) ? intval($curr_ver) : 0; - $valid_ver = false; - if ($curr_ver < $min_ver) { - http_response_code(501); - $api_resp = array("status" => "not implemented", "code" => 501, "return" => 5); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - echo json_encode($api_resp) . PHP_EOL; - die(); - } -} - -// Check if server IP is allowed to answer API calls. Redirects to login if not -function api_whitelist_check() { - global $err_lib, $config; - $allowed = false; - $pkg_data = get_api_configuration(); - $pkg_id = $pkg_data[0]; - $pkg_conf = $pkg_data[1]; - $if_conf = $config["interfaces"]; - $whitelist = array("any"); - $srv_ip = $_SERVER["SERVER_ADDR"]; - - // Check that we have a package configuration - if (is_numeric($pkg_id)) { - // Override defaults with user specified config - if (isset($pkg_conf["allowed_interfaces"])) { - $allow_ifs = $pkg_conf["allowed_interfaces"]; - if (strpos($allow_ifs, ",") !== false) { - $whitelist = explode(",", $allow_ifs); - } else { - $whitelist = array($allow_ifs); - } - } else { - $config["installedpackages"]["package"][$pkg_id]["allowed_interfaces"] = "any"; - write_config(); - } - // Check if our server IP is in our whitelist - foreach ($whitelist as $wif) { - $if_info = get_interface_info($wif); - // Check if our server IP is a valid if address, localhost, or any - if ($srv_ip === $if_info["ipaddr"]) { - $allowed = true; - break; - } elseif ($srv_ip === $if_info["ipaddrv6"]) { - $allowed = true; - break; - } elseif (in_array($srv_ip, ["::1", "127.0.0.1", "localhost"]) and $wif === "localhost") { - $allowed = true; - break; - }elseif ($wif === "any") { - $allowed = true; - break; - } - } - } - if (!$allowed) { - unset($_SESSION); - $api_resp = array("status" => "forbidden", "code" => 403, "return" => 6); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - http_response_code(403); - echo json_encode($api_resp).PHP_EOL; - die; - } -} - -// Search JSON response for specific fields -function api_extended_search($base_data, $search_data) { - global $err_lib; - $search_failed = false; - // If client requests an extended search - if (is_array($base_data) and is_array($search_data)) { - $target_item = end($search_data); // Save our last array item - // Loop through each attribute in the search list and check if it exists - foreach ($search_data as &$df) { - if (array_key_exists($df, $base_data)) { - // If this attribute is our target item, format and return the value - if ($df === $target_item) { - if (!is_array($base_data[$df])) { - $base_data = array($df => $base_data[$df]); - } else { - $base_data = $base_data[$df]; - } - break; - } else { - // Check if this item is an array, if so search the nested array. Otherwise failed - if (is_array($base_data[$df])) { - $base_data = $base_data[$df]; - } else { - $search_failed = true; - break; - } - } - } else { - $search_failed = true; - break; - } - } - } else { - $search_failed = true; - } - // Check if we failed to find our search attributes - if ($search_failed === true) { - $api_resp = array("status" => "not found", "code" => 404, "return" => 7); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - http_response_code(404); - echo json_encode($api_resp) . PHP_EOL; - die(); - } - return $base_data; -} - -// Check our local pfSense version -function get_pfsense_version() { - # VARIABLES - $ver_path = "/etc/version"; // Assign the path to our version file - $ver_patch_path = "/etc/version.patch"; // Assign the path to our version patch file - $ver_bt_path = "/etc/version.buildtime"; // Assign the path to our version build time file - $ver_lc_path = "/etc/version.lastcommit"; // Assign the path to our version last commit file - $ver_data = array(); // Init an empty array for our version data - # RUN TIME - // Check that our files exist, if so read the files. Otherwise return error - if (file_exists($ver_path)) { - $ver_file = fopen($ver_path, "r"); // Open our file - $ver = str_replace(PHP_EOL, "", fread($ver_file, filesize($ver_path))); // Save our version data - $ver_data["version"] = $ver; // Save to array - } - if (file_exists($ver_patch_path)) { - $ver_patch_file = fopen($ver_patch_path, "r"); // Open our file - $ver_patch = str_replace(PHP_EOL, "", fread($ver_patch_file, filesize($ver_patch_path))); // Save patch - $ver_data["patch"] = $ver_patch; // Save to array - } - if (file_exists($ver_bt_path)) { - $ver_bt_file = fopen($ver_bt_path, "r"); // Open our file - $ver_bt = str_replace(PHP_EOL, "", fread($ver_bt_file, filesize($ver_bt_path))); // Save bt data - $ver_data["buildtime"] = $ver_bt; // Save to array - } - if (file_exists($ver_lc_path)) { - $ver_lc_file = fopen($ver_lc_path, "r"); // Open our file - $ver_lc = str_replace(PHP_EOL, "", fread($ver_lc_file, filesize($ver_lc_path))); // Save bt data - $ver_data["lastcommit"] = $ver_lc; // Save to array - } - $ver_data["program"] = floatval(str_replace(".", "", explode("-", $ver)[0]).".".$ver_patch); - return $ver_data; -} - -// Parse our ARP table into an array -function get_arp_table() { - // Local variables - $arp_cmd = "arp -an"; // Assign the command which reads our ARP table - exec($arp_cmd, $arp_data); // Output our ARP table into a string - $arp_table = array(); // Init our ARP table array - // Loop through each line of our ARP data and parse into our array - foreach ($arp_data as $arp_line) { - $elements = explode(' ', $arp_line, 7); - $arp_entry = array(); - $arp_entry['ip'] = trim(str_replace(array('(', ')'), '', $elements[1])); - $arp_entry['mac'] = trim($elements[3]); - $arp_entry['interface'] = trim($elements[5]); - $arp_entry['status'] = trim(substr($elements[6], 0, strrpos($elements[6], ' '))); - $arp_entry['linktype'] = trim(str_replace(array('[', ']'), '', strrchr($elements[6], ' '))); - $arp_table[] = $arp_entry; - } - return $arp_table; -} - -// Pull a single ARP entry value from our ARP table -function get_arp_entry($search, $value) { - // Local variables - $arp_table = get_arp_table(); // Pull our ARP table - $arp_match = []; // Init our match array - // Loop through ARP table and look for matches - foreach ($arp_table as $arp_ent) { - if ($arp_ent[$search] === $value) { - $arp_match = $arp_ent; - } - } - return $arp_match; -} - -// Reload our unbound configuration, restart associated services and clear config locks -function unbound_reload_config() { - $reload_unbound = 0; - $reload_unbound |= services_unbound_configure(); - // Check if application was successful - if ($reload_unbound === 0) { - system_resolvconf_generate(); // Update resolveconf - system_dhcpleases_configure(); // Update DHCPD - clear_subsystem_dirty("unbound"); - return true; - } -} - -// Check if a DNS Resolver (Unbound) host override already exists -function unbound_host_override_exists($hostname, $domain) { - // Local variables - global $err_lib, $config; - $curr_hosts = array(); - $host_exists = false; - // Check if host override already exists - if (array_key_exists("hosts", $config["unbound"])) { - $curr_hosts = $config["unbound"]["hosts"]; - } - foreach ($curr_hosts as $host_ent) { - if ($host_ent["host"] === $hostname and $host_ent["domain"] === $domain) { - $host_exists = true; - break; - } - if (is_array($host_ent["aliases"])) { - foreach ($host_ent["aliases"]["item"] as $alias_ent) { - if ($alias_ent["host"] === $hostname and $alias_ent["domain"] === $domain) { - $host_exists = true; - break; - } - } - } - } - return $host_exists; -} - -// Check user's DNS Resolver (Unbound) host override alias input for errors -function unbound_parse_aliases($aliases) { - global $err_lib; - $aliases_fin = ""; // Default our alias value to blank string - if (is_array($aliases)) { - $a_count = 0; - $aliases_fin = array(); - foreach ($aliases as $alias_ent) { - // Check that each alias has a valid hostname - if (array_key_exists("host", $alias_ent) and is_string($alias_ent["host"])) { - $aliases_fin[$a_count] = array("host" => trim($alias_ent["host"])); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2007); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - http_response_code($api_resp["code"]); - echo json_encode($api_resp) . PHP_EOL; - die(); - } - // Check that each alias has a valid domain name - if (array_key_exists("domain", $alias_ent) and is_string($alias_ent["domain"])) { - $aliases_fin[$a_count]["domain"] = trim($alias_ent["domain"]); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2008); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - http_response_code($api_resp["code"]); - echo json_encode($api_resp) . PHP_EOL; - die(); - } - // Check for alias description under descr key - if (array_key_exists("descr", $alias_ent) and is_string($alias_ent["descr"])) { - if (is_string($alias_ent["descr"])) { - $aliases_fin[$a_count]["description"] = $alias_ent["descr"]; - } - } - // Check for alias description under description key - if (array_key_exists("description", $alias_ent) and is_string($alias_ent["description"])) { - if (is_string($alias_ent["description"])) { - $aliases_fin[$a_count]["description"] = $alias_ent["description"]; - } - } - // Increase our counter - $a_count++; - } - // Check if our aliases already exist - foreach ($aliases_fin as $af) { - if (unbound_host_override_exists($af["host"], $af["domain"])) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2009); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - http_response_code($api_resp["code"]); - echo json_encode($api_resp) . PHP_EOL; - die(); - } - } - // Nest our aliases under the item key - $aliases_fin = array("item" => $aliases_fin); - } - // Return our parsed aliases - return $aliases_fin; -} - -// Duplicate function from /firewall_aliases.php: accept input and check if alias exists -function alias_find_references($section, $field, $origname, &$is_alias_referenced, &$referenced_by) { - global $err_lib, $config; - if (!$origname || $is_alias_referenced) { - return; - } - $sectionref = &$config; - foreach ($section as $sectionname) { - if (is_array($sectionref) && isset($sectionref[$sectionname])) { - $sectionref = &$sectionref[$sectionname]; - } else { - return; - } - } - if (is_array($sectionref)) { - foreach ($sectionref as $itemkey => $item) { - $fieldfound = true; - $fieldref = &$sectionref[$itemkey]; - foreach ($field as $fieldname) { - if (is_array($fieldref) && isset($fieldref[$fieldname])) { - $fieldref = &$fieldref[$fieldname]; - } else { - $fieldfound = false; - break; - } - } - if ($fieldfound && $fieldref == $origname) { - $is_alias_referenced = true; - if (is_array($item)) { - $referenced_by = $item['descr']; - } - break; - } - } - } -} - -// Input an alias name and check if the alias exists -function alias_in_use($alias_name) { - $is_alias_referenced = false; - $referenced_by = false; - // Firewall rules - alias_find_references(array('filter', 'rule'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('filter', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('filter', 'rule'), array('source', 'port'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('filter', 'rule'), array('destination', 'port'), $alias_name, $is_alias_referenced, $referenced_by); - // NAT Rules - alias_find_references(array('nat', 'rule'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'rule'), array('source', 'port'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'rule'), array('destination', 'port'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'rule'), array('target'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'rule'), array('local-port'), $alias_name, $is_alias_referenced, $referenced_by); - // NAT 1:1 Rules - //alias_find_references(array('nat', 'onetoone'), array('external'), $alias_name, $is_alias_referenced, $referenced_by); - //alias_find_references(array('nat', 'onetoone'), array('source', 'address'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'onetoone'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); - // NAT Outbound Rules - alias_find_references(array('nat', 'outbound', 'rule'), array('source', 'network'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'outbound', 'rule'), array('sourceport'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'outbound', 'rule'), array('destination', 'address'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'outbound', 'rule'), array('dstport'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('nat', 'outbound', 'rule'), array('target'), $alias_name, $is_alias_referenced, $referenced_by); - // Alias in an alias - alias_find_references(array('aliases', 'alias'), array('address'), $alias_name, $is_alias_referenced, $referenced_by); - // Load Balancer - alias_find_references(array('load_balancer', 'lbpool'), array('port'), $alias_name, $is_alias_referenced, $referenced_by); - alias_find_references(array('load_balancer', 'virtual_server'), array('port'), $alias_name, $is_alias_referenced, $referenced_by); - // Static routes - alias_find_references(array('staticroutes', 'route'), array('network'), $alias_name, $is_alias_referenced, $referenced_by); - return $is_alias_referenced; -} - -// Input a physical interface ID, or a descriptive interface name, and return the pfSense interface ID (lan,wan,optx) -function get_pfsense_if_id($interface) { - // Variables - global $err_lib, $config; - // Loop through our config and check each interface for a physical ID match - foreach ($config["interfaces"] as $ifid => $ife) { - // Check that we have a configured interface ID first - if (array_key_exists("if", $ife)) { - // Check if the interface id matches - if ($interface === $ife["if"]) { - return $ifid; // Return our pfSense interface ID - break; // Break loop just in case - } - } - // Check that we have a configured interface descr first - if (array_key_exists("descr", $ife)) { - // Check if the interface descr matches - if ($interface === $ife["descr"]) { - return $ifid; // Return our pfSense interface ID - break; // Break loop just in case - } - } else { - // Check if the pfSense interface ID matches - if (strtolower($interface) === $ifid) { - return $ifid; // Return our pfSense interface ID - break; // Break loop just in case - } - } - } -} - -// Check if input is valid for rule source and destination -function is_valid_rule_addr($addr, $direction) { - // Variables - $addr_types = array("any", "pppoe", "l2tp"); // Array of special src/dst types - $ret_val = array("valid" => true, "data" => array()); - // Check if our source values are valid - if (is_string($addr)) { - // Check src/dst is negated - if (str_starts_with("!", $addr)) { - $addr_not = true; - $addr = str_replace("!", "", $addr); - } - // Check if our source data is valid - $addr_if = str_replace("ip", "", $addr); // Save seperate variable to check for interface sourcees - if (is_ipaddr($addr) or is_subnet($addr)) { - $ret_val["data"] = array($direction => array("address" => $addr)); - } elseif (is_alias($addr)) { - $ret_val["data"] = array($direction => array("address" => $addr)); - } elseif (get_pfsense_if_id($addr_if)) { - $addr_pfif = get_pfsense_if_id($addr_if); // Save our interface pfid - // If source was interface address (ending in ip), otherwise assume entire subnet - if (str_replace($addr_if, "", $addr) === "ip") { - $ret_val["data"] = array($direction => array("network" => $addr_pfif . "ip")); - } else { - $ret_val["data"] = array($direction => array("network" => $addr_pfif)); - } - } elseif (in_array($addr, $addr_types)) { - if ($addr === "any") { - $ret_val["data"] = array($direction => array("any" => "")); - } else { - $ret_val["data"] = array($direction => array("network" => $addr)); - } - } else { - $ret_val["valid"] = false; - } - // If source is negated, add not to our array - if ($addr_not) { - $ret_val["data"][$direction]["not"] = ""; - } - } else { - $ret_val["valid"] = false; - } - return $ret_val; -} - -// Sorts filter rules by specified criteria and reloads the filter -function sort_firewall_rules($mode=null, $data=null) { - // Variables - global $err_lib, $config; - $sort_arr = []; - $master_arr = []; - foreach ($config["filter"]["rule"] as $idx => $fre) { - $curr_iface = $fre["interface"]; // Save our current entries interface - // Create our interface array if does not exist - if (!isset($sort_arr[$curr_iface])) { - $sort_arr[$curr_iface] = []; - } - // Check if user requested this rule to be placed at the top of array - if ($mode === "top" and $idx === $data) { - array_unshift($sort_arr[$curr_iface], $fre); - } else { - $sort_arr[$curr_iface][] = $fre; - } - } - foreach ($sort_arr as $if) { - foreach ($if as $rule) { - $master_arr[] = $rule; - } - } - $config["filter"]["rule"] = $master_arr; -} - -// Sorts nat rules by specified criteria and reloads the filter -function sort_nat_rules($mode=null, $data=null) { - // Variables - global $config; - $sort_arr = []; - $master_arr = []; - foreach ($config["nat"]["rule"] as $idx => $fre) { - $curr_iface = $fre["interface"]; // Save our current entries interface - // Create our interface array if does not exist - if (!isset($sort_arr[$curr_iface])) { - $sort_arr[$curr_iface] = []; - } - // Check if user requested this rule to be placed at the top of array - if ($mode === "top" and $idx === $data) { - array_unshift($sort_arr[$curr_iface], $fre); - } else { - $sort_arr[$curr_iface][] = $fre; - } - } - foreach ($sort_arr as $if) { - foreach ($if as $rule) { - $master_arr[] = $rule; - } - } - $config["nat"]["rule"] = $master_arr; -} - -// Sort our state table -function sort_state_table() { - // Local variables - $states = shell_exec("/sbin/pfctl -s state"); - $state_table = array(); - // Parse our output - $state_rows = explode("\n", $states); - foreach ($state_rows as $id => $data) { - $data = preg_replace('!\s+!', ' ', $data); // Remove extra whitespace - $data_fields = explode(" ", $data); // Split on whitespace - if (count($data_fields) > 1) { - $state_table[$id] = array(); - $state_table[$id]["interface"] = $data_fields[0]; - $state_table[$id]["protocol"] = $data_fields[1]; - $state_table[$id]["source"] = ($data_fields[3] === "->") ? $data_fields[2] : $data_fields[4]; - $state_table[$id]["destination"] = ($data_fields[3] === "->") ? $data_fields[4] : $data_fields[2]; - $state_table[$id]["status"] = $data_fields[5]; - } - } - return $state_table; -} - -// Checks if inputted routing gateway exists -function is_gateway($gw) { - // Local variables - global $err_lib, $config; - $gw_config = $config["gateways"]["gateway_item"]; - $gw_exists = false; - // Check that we have gateways configured - if (is_array($gw_config)) { - // Loop through each gateway and see if name matches - foreach ($gw_config as $gw_item) { - if ($gw === $gw_item["name"]) { - $gw_exists = true; - break; - } - } - } - return $gw_exists; -} - -// Checks if an IP is in use elsewhere in our configuration -function is_ip_in_use($ip) { - global $err_lib, $config; - $vip_conf = $config["virtualip"]["vip"]; - $if_conf = $config["interfaces"]; - // Check if IP is used in our virtual IP configuration - foreach ($vip_conf as $vip) { - if ($ip === $vip["subnet"]) { - return true; - } - } - // Check if IP is used in our interface configuration - foreach ($if_conf as $iface) { - if ($ip === $iface["ipaddr"]) { - return true; - } elseif ($ip === $iface["ipaddrv6"]) { - return true; - } - } - return false; -} - -// Check if a VHID already exists -function vhid_exists($vhid) { - global $err_lib, $config; - $vhid_config = $config["virtualip"]["vip"]; - $vhid_exists = false; - # Loop through our virutal IPs and check if this VHID already exists - foreach ($vhid_config as $vip) { - if (intval($vip["vhid"]) === intval($vhid)) { - $vhid_exists = true; - } - } - return $vhid_exists; -} - -// Find an available virtual IP vhid -function next_vhid() { - global $err_lib, $config; - $vhid_config = $config["virtualip"]["vip"]; - $ret_vhid = null; - # Loop through our range of valid VHID ids - foreach (range(1, 255) as $idx) { - $vhid_exists = false; - # Loop through our virutal IPs and check if this VHID already exists - foreach ($vhid_config as $vip) { - if (intval($vip["vhid"]) === intval($idx)) { - $vhid_exists = true; - } - } - // Check if our VHID was already used - if (!$vhid_exists) { - $ret_vhid = $idx; - break; - } - } - return $ret_vhid; -} - -// Apply changes to virtual IPs -function apply_virtual_ip($vip_ent) { - $check_carp = false; - if (is_array($vip_ent)) { - foreach ($vip_ent as $vid => $ovip) { - if (!empty($ovip)) { - interface_vip_bring_down($ovip); - } - if ($vip_ent) { - switch ($vip_ent['mode']) { - case "ipalias": - interface_ipalias_configure($vip_ent); - break; - case "proxyarp": - interface_proxyarp_configure($vip_ent['interface']); - break; - case "carp": - $check_carp = true; - interface_carp_configure($vip_ent); - break; - default: - break; - } - } - } - } - /* Before changing check #4633 */ - if ($check_carp === true && !get_carp_status()) { - set_single_sysctl("net.inet.carp.allow", "1"); - } - filter_configure(); -} - -// Remove virtual IP and bring down virtual interface -function bring_down_virtual_ip($vip_ent, $id) { - global $err_lib, $config; - if ($vip_ent['mode'] == "proxyarp") { - $viface = $vip_ent['interface']; - unset($config["virtualip"]["vip"][$id]); - interface_proxyarp_configure($viface); - } else { - interface_vip_bring_down($vip_ent); - unset($config["virtualip"]["vip"][$id]); - } - if (count($config['virtualip']['vip']) == 0) { - unset($config['virtualip']['vip']); - } -} - -// Get a complete config list of ALL interfaces. Based off interfaces_assign.php -function get_all_avail_interfaces() { - // Local variables - global $err_lib, $config; - $base_ifs = get_interface_list(); // Get our base interface list, this will be populated with all ifs - // Add wireless ifs to our array - if (is_array($config['wireless']['clone']) && count($config['wireless']['clone'])) { - foreach ($config['wireless']['clone'] as $clone) { - $base_ifs[$clone['cloneif']] = $clone; - $base_ifs[$clone['cloneif']]['is_wlclone'] = true; - } - } - // Add VLAN ifs to our array - if (is_array($config['vlans']['vlan']) && count($config['vlans']['vlan'])) { - //$timea = microtime(true); - foreach ($config['vlans']['vlan'] as $vlan) { - $base_ifs[$vlan['vlanif']] = $vlan; - $base_ifs[$vlan['vlanif']]['is_vlan'] = true; - } - } - // Add bridge ifs to our array - if (is_array($config['bridges']['bridged']) && count($config['bridges']['bridged'])) { - foreach ($config['bridges']['bridged'] as $bridge) { - $base_ifs[$bridge['bridgeif']] = $bridge; - $base_ifs[$bridge['bridgeif']]['is_bridge'] = true; - } - } - // Add GIF ifs to our array - if (is_array($config['gifs']['gif']) && count($config['gifs']['gif'])) { - foreach ($config['gifs']['gif'] as $gif) { - $base_ifs[$gif['gifif']] = $gif; - $base_ifs[$gif['gifif']]['is_gif'] = true; - } - } - // Add GRE ifs to our array - if (is_array($config['gres']['gre']) && count($config['gres']['gre'])) { - foreach ($config['gres']['gre'] as $gre) { - $base_ifs[$gre['greif']] = $gre; - $base_ifs[$gre['greif']]['is_gre'] = true; - } - } - // Add LAGG ifs to our array - if (is_array($config['laggs']['lagg']) && count($config['laggs']['lagg'])) { - foreach ($config['laggs']['lagg'] as $lagg) { - $base_ifs[$lagg['laggif']] = $lagg; - $base_ifs[$lagg['laggif']]['is_lagg'] = true; - /* LAGG members cannot be assigned */ - $lagifs = explode(',', $lagg['members']); - foreach ($lagifs as $lagif) { - if (isset($base_ifs[$lagif])) { - unset($base_ifs[$lagif]); - } - } - } - } - // Add QinQ ifs to our array - if (is_array($config['qinqs']['qinqentry']) && count($config['qinqs']['qinqentry'])) { - foreach ($config['qinqs']['qinqentry'] as $qinq) { - $base_ifs["{$qinq['vlanif']}"]['descr'] = "VLAN {$qinq['tag']} on {$qinq['if']}"; - $base_ifs["{$qinq['vlanif']}"]['is_qinq'] = true; - /* QinQ members */ - $qinqifs = explode(' ', $qinq['members']); - foreach ($qinqifs as $qinqif) { - $base_ifs["{$qinq['vlanif']}.{$qinqif}"]['descr'] = "QinQ {$qinqif} on VLAN {$qinq['tag']} on {$qinq['if']}"; - $base_ifs["{$qinq['vlanif']}.{$qinqif}"]['is_qinq'] = true; - } - } - } - // Add PPP ifs to our array - if (is_array($config['ppps']['ppp']) && count($config['ppps']['ppp'])) { - foreach ($config['ppps']['ppp'] as $pppid => $ppp) { - $if_name = $ppp['if']; - $base_ifs[$if_name] = $ppp; - $base_ifs[$if_name]['is_ppp'] = true; - $ports_base = basename($ppp['ports']); - if (isset($ppp['descr'])) { - $base_ifs[$if_name]['descr'] = strtoupper($ppp['if']). "({$ports_base}) - {$ppp['descr']}"; - } else if (isset($ppp['username'])) { - $base_ifs[$if_name]['descr'] = strtoupper($ppp['if']). "({$ports_base}) - {$ppp['username']}"; - } else { - $base_ifs[$if_name]['descr'] = strtoupper($ppp['if']). "({$ports_base})"; - } - } - } - // Add OpenVPN descriptions to our array - $ovpn_descrs = array(); - if (is_array($config['openvpn'])) { - if (is_array($config['openvpn']['openvpn-server'])) { - foreach ($config['openvpn']['openvpn-server'] as $s) { - $if_name = "ovpns{$s['vpnid']}"; - $base_ifs[$if_name] = $s; - $ovpn_descrs[$s['vpnid']] = $s['description']; - } - } - if (is_array($config['openvpn']['openvpn-client'])) { - foreach ($config['openvpn']['openvpn-client'] as $c) { - $if_name = "ovpnc{$c['vpnid']}"; - $base_ifs[$if_name] = $c; - $ovpn_descrs[$c['vpnid']] = $c['description']; - } - } - } - // Add IPsec descriptions to our array - global $err_lib, $ipsec_descrs; - $ipsec_descrs = interface_ipsec_vti_list_all(); - foreach ($ipsec_descrs as $ifname => $ifdescr) { - $base_ifs[$ifname] = array('descr' => $ifdescr); - } - // Loop through our array and check if interface is in use - foreach ($base_ifs as $pid => $conf) { - $pf_id = get_pfsense_if_id($pid); // Try to convert to pfSense interface ID - // Check if our pfSense ID was found - if (get_pfsense_if_id($pid)) { - $base_ifs[$pid]["in_use"] = $pf_id; - } - } - return $base_ifs; -} - -// Get available media options for a given interface. Modified from interfaces.php -function get_if_media_options($interface, $physical_if=false) { - // Local variables - global $err_lib, $config; - $interface = ($physical_if !== true) ? get_pfsense_if_id($interface) : $interface; - $interface = ($physical_if !== true) ? $config['interfaces'][$interface]['if'] : $interface; - $mediaopts_list = array(); - exec("/sbin/ifconfig -m $interface | grep \"media \"", $mediaopts); - foreach ($mediaopts as $mediaopt) { - preg_match("/media (.*)/", $mediaopt, $matches); - if (preg_match("/(.*) mediaopt (.*)/", $matches[1], $matches1)) { - // there is media + mediaopt like "media 1000baseT mediaopt full-duplex" - array_push($mediaopts_list, $matches1[1] . " " . $matches1[2]); - } else { - // there is only media like "media 1000baseT" - array_push($mediaopts_list, $matches[1]); - } - } - return $mediaopts_list; -} - -// Get our next available pfSense interface ID -function get_next_pfsense_if_id() { - // Local variables - global $err_lib, $config; - $curr_ifs = $config["interfaces"]; - // Check if we have our `wan` or `lan` pf IDs in use, if so, find the next OPTx id - if (!array_key_exists("wan", $curr_ifs)) { - return "wan"; - } elseif (!array_key_exists("lan", $curr_ifs)) { - return "lan"; - } else { - // Loop until we find an unused OPTx interface - foreach (range(1, 2000) as $count) { - // Check if this OPTx ID exists - $optx = "opt".strval($count); - if (!array_key_exists($optx, $curr_ifs)) { - return $optx; - } - } - } -} - -// Returns a list of dynamically configured IPv6 interfaces. Modified from interfaces.php function. -function get_ipv6_if_list() { - global $err_lib, $config, $section; - $list = array('' => ''); - $interfaces = get_configured_interface_with_descr(true); - $dyn_v6_ifs = array(); - foreach ($interfaces as $iface => $ifacename) { - switch ($config['interfaces'][$iface]['ipaddrv6']) { - case "6to4": - case "6rd": - case "dhcp6": - $dyn_v6_ifs[$iface] = array( - 'name' => $ifacename, - 'ipv6_num_prefix_ids' => pow(2, (int) calculate_ipv6_delegation_length($iface)) - 1); - break; - default: - continue 2; - } - } - return($dyn_v6_ifs); -} - -// Apply a new interface configuration -function apply_interface_config($if_conf) { - // Local variables - global $err_lib, $config; - $vlan_redo = false; - // Check that our if configuration is an array - if (is_array($if_conf)) { - foreach ($if_conf as $if_apply => $if_go) { - if (isset($config['interfaces'][$if_apply]['enable'])) { - interface_bring_down($if_apply, false, $if_go); - interface_configure($if_apply, true); - if ($config['interfaces'][$if_apply]['ipaddrv6'] == "track6") { - // Configure IPv6 track6 type if present - $wan_cfg = $config['interfaces'][$if_apply]; - interface_track6_configure($if_apply, $wan_cfg, true); - } - } else { - interface_bring_down($if_apply, true, $if_go); - // Restart DHCPD if enabled - if (isset($config['dhcpd'][$if_apply]['enable']) || - isset($config['dhcpdv6'][$if_apply]['enable'])) { - services_dhcpd_configure(); - } - } - // Check if VLANs are configured for this interface, if so, reapply them - if (interface_has_clones(get_real_interface($if_apply))) { - $vlan_redo = true; - } - } - } - // Check if DHCP needs to be reloaded - if ($if_conf['ipaddr'] == "dhcp") { - kill_dhclient_process($if_conf['if']); - } - if ($if_conf['ipaddrv6'] == "dhcp6") { - kill_dhcp6client_process($if_conf['if'],true); - } - // Reapply VLANs if necessary - if ($vlan_redo) { - interfaces_vlan_configure(); - } - // Restart services that may reference IP - services_snmpd_configure(); - setup_gateways_monitor(); - filter_configure(); - enable_rrd_graphing(); - system_routing_configure(); - // Return true or false if configuration was successful - return true; -} - -// Delete an interface -function destroy_interface($id) { - // Local variables - global $err_lib, $config; - $err_msg = ""; - $success = false; - if ($id === "wan") { - $err_msg = 3042; - } elseif (link_interface_to_group($id)) { - $err_msg = 3043; - } elseif (link_interface_to_bridge($id)) { - $err_msg = 3044; - } elseif (link_interface_to_gre($id)) { - $err_msg = 3045; - } elseif (link_interface_to_gif($id)) { - $err_msg = 3046; - } elseif (interface_has_queue($id)) { - $err_msg = 3047; - } else { - unset($config['interfaces'][$id]['enable']); - $realid = get_real_interface($id); - interface_bring_down($id); // Bring down interface - unset($config['interfaces'][$id]); // Delete our interface from configuration - // Remove DHCP config for interface - if (is_array($config['dhcpd']) && is_array($config['dhcpd'][$id])) { - unset($config['dhcpd'][$id]); - services_dhcpd_configure('inet'); - } - // Removed interface config for dhcp6 - if (is_array($config['dhcpdv6']) && is_array($config['dhcpdv6'][$id])) { - unset($config['dhcpdv6'][$id]); - services_dhcpd_configure('inet6'); - } - // Remove ACL for interface - if (count($config['filter']['rule']) > 0) { - foreach ($config['filter']['rule'] as $x => $rule) { - if ($rule['interface'] == $id) { - unset($config['filter']['rule'][$x]); - } - } - } - // Remove NAT config for interface - if (is_array($config['nat']['rule']) && count($config['nat']['rule']) > 0) { - foreach ($config['nat']['rule'] as $x => $rule) { - if ($rule['interface'] == $id) { - unset($config['nat']['rule'][$x]['interface']); - } - } - } - $change_note = " Deleted interface via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - // Disable DHCP if last interface - if ($config['interfaces']['lan'] && $config['dhcpd']['wan']) { - unset($config['dhcpd']['wan']); - } - // Update VLAN assignments - link_interface_to_vlans($realid, "update"); - // Write success return - $success = true; - } - return array("status" => $success, "msg" => $err_msg); -} - -// Check if CARP is enabled for disabled -function is_carp_enabled() { - // Check current CARP status - $status = get_single_sysctl('net.inet.carp.allow'); - $enabled = boolval(intval($status) > 0); - return $enabled; -} - -// Check each CARP interface's status -function get_carp_if_status() { - // Local variables - global $err_lib, $config; - $carp_if_stats = []; - $carp_enabled = is_carp_enabled(); - foreach ($config['virtualip']['vip'] as $carp) { - if ($carp['mode'] == "carp") { - $carp_if_ent = []; - $carp_if_ent["interface"] = $carp["interface"]; - $carp_if_ent["vhid"] = $carp['vhid']; - $carp_if_ent["subnet"] = $carp['subnet']; - $carp_if_ent["subnet_bits"] = $carp['subnet_bits']; - $status = get_carp_interface_status("_vip{$carp['uniqid']}"); - if ($carp_enabled == false) { - $carp_if_ent["status"] = "disabled"; - } else { - if ($status == "MASTER") { - $carp_if_ent["status"] = "master"; - } else if ($status == "BACKUP") { - $carp_if_ent["status"] = "backup"; - } else if ($status == "INIT") { - $carp_if_ent["status"] = "init"; - } - } - // Add config to our array - $carp_if_stats[] = $carp_if_ent; - } - } - // Return our status - return $carp_if_stats; -} - -// Enables CARP interfaces -function enable_carp($enable) { - // Local variables - global $config; - $vip_arr = $config['virtualip']['vip']; - $no_action = (is_carp_enabled() === $enable) ? true : false; // Check if a change is even requried - // Disable if $enable is false, enable if $enable is true - if (!$no_action and $enable === false) { - set_single_sysctl('net.inet.carp.allow', '0'); - foreach ($vip_arr as $vip) { - if ($vip['mode'] != "carp" && $vip['mode'] != "ipalias") - continue; - if ($vip['mode'] == "ipalias" && substr($vip['interface'], 0, 4) != "_vip") - continue; - interface_vip_bring_down($vip); - } - } elseif (!$no_action and $enable === true) { - foreach ($vip_arr as $vip) { - switch ($vip['mode']) { - case "carp": - interface_carp_configure($vip); - break; - case 'ipalias': - if (substr($vip['interface'], 0, 4) == "_vip") { - interface_ipalias_configure($vip); - } - break; - } - } - interfaces_sync_setup(); - set_single_sysctl('net.inet.carp.allow', '1'); - } -} - -// Checks if an authentication server exists by name -function is_authentication_server($name) { - global $err_lib, $config; - foreach ($config["system"]["authserver"] as $as) { - $reserved_names = [$as["name"], "Local_Database", "local", "LOCAL", "Local"]; - if (in_array($name, $reserved_names)) { - return true; - } - } - return false; -} diff --git a/pfSense-pkg-API/files/etc/inc/apicalls.inc b/pfSense-pkg-API/files/etc/inc/apicalls.inc deleted file mode 100644 index 584ab2611..000000000 --- a/pfSense-pkg-API/files/etc/inc/apicalls.inc +++ /dev/null @@ -1,6866 +0,0 @@ - "ok", "code" => 200, "return" => 0, "message" => "", "data" => ["token" => $jwt]); - } - } else { - $api_resp = array("status" => "forbidden", "code" => 403, "return" => 9, "message" => $err_lib[9], "data" => []); - } - return $api_resp; -} - -function api_status_carp() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-carp"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - $carp_status = []; - $carp_status["enable"] = is_carp_enabled(); - $carp_status["maintenance_mode"] = isset($config["virtualip_carp_maintenancemode"]); - $carp_status["interfaces"] = get_carp_if_status(); - if (isset($client_params['search'])) { - $search = $client_params['search']; - $carp_status = api_extended_search($carp_status, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $carp_status); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_status_carp_modify() { - # VARIABLES - global $err_lib, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-carp"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $carp_stat_arr = []; // Init array to track what changes were made - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - // Check if user specified enable value - if (isset($client_params['enable'])) { - // Check if value is true or false - if (boolval($client_params['enable'])) { - $enable = true; - } else { - $enable = false; - } - $carp_stat_arr["enable"] = $enable; - } - // Check if user specified maintenance mode value - if (isset($client_params['maintenance_mode'])) { - // Check if value is true or false - if (boolval($client_params['maintenance_mode'])) { - $mm_enable = true; - } else { - $mm_enable = false; - } - $carp_stat_arr["maintenance_mode"] = $mm_enable; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "CARP ENABLED:" . PHP_EOL; - echo var_dump($enable) . PHP_EOL; - echo "CARP MAINTENANCE MODE ENABLED:" . PHP_EOL; - echo var_dump($mm_enable) . PHP_EOL; - } - // Add our CARP settings - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - interfaces_carp_set_maintenancemode($mm_enable); // Set our maintenance mode value - enable_carp($enable); // Set our CARP enable value - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $carp_stat_arr); - $api_resp["message"] = "Successfully modified CARP settings"; - $api_resp["data"] = $carp_stat_arr; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_nat() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $nat_array = array(); // Init our array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a virtual IP configuration - if (!empty($config["nat"])) { - $nat_array = $config["nat"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $nat_array = api_extended_search($nat_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $nat_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_nat_portforwards() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-nat-portforward"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $nat_array = array(); // Init our array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a virtual IP configuration - if (!empty($config["nat"]["rule"])) { - $nat_array = $config["nat"]["rule"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $nat_array = api_extended_search($nat_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $nat_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_nat_portforwards_add() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-nat-portforward-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $next_rule_id = count($config["nat"]["rule"]); // Save our next rule ID - $allowed_nat_ref = array("enable", "disable", "purenat"); // Save our allow NAT reflection types - // Array of supported protocols - $allowed_prot = array( - "tcp", - "udp", - "tcp/udp", - "icmp", - "esp", - "ah", - "gre", - "ipv6", - "igmp", - "pim", - "ospf" - ); - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['interface'])) { - $interface = $client_params['interface']; - $interface = get_pfsense_if_id($interface); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['protocol'])) { - $protocol = $client_params['protocol']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4001); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['target'])) { - $localip = $client_params['target']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4002); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['local-port'])) { - $localport = $client_params['local-port']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4003); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['src'])) { - $src = $client_params['src']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4004); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['srcport'])) { - $srcport = $client_params['srcport']; - } - if (isset($client_params['dst'])) { - $dst = $client_params['dst']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4005); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['dstport'])) { - $dstport = $client_params['dstport']; - } - if (isset($client_params['disabled'])) { - if ($client_params['disabled']) { - $disabled = true; - } - } - if (isset($client_params['nordr'])) { - if ($client_params['nordr']) { - $nordr = true; - } - } - if (isset($client_params['nosync'])) { - if ($client_params['nosync'] === true) { - $nosync = true; - } - } - if (isset($client_params['top'])) { - if ($client_params['top']) { - $top = "top"; - } - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } - if (isset($client_params['natreflection'])) { - $natreflection = $client_params['natreflection']; - } - - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "INTERFACE:" . PHP_EOL; - echo var_dump($interface) . PHP_EOL; - echo "PROTOCOL:" . PHP_EOL; - echo var_dump($protocol) . PHP_EOL; - echo "REDIRECT IP:" . PHP_EOL; - echo var_dump($localip) . PHP_EOL; - echo "REDIRECT PORT:" . PHP_EOL; - echo var_dump($localport) . PHP_EOL; - echo "SOURCE:" . PHP_EOL; - echo var_dump($src) . PHP_EOL; - echo "SOURCE PORT:" . PHP_EOL; - echo var_dump($srcport) . PHP_EOL; - echo "DESTINATION:" . PHP_EOL; - echo var_dump($dst) . PHP_EOL; - echo "DESTINATION PORT:" . PHP_EOL; - echo var_dump($dstport) . PHP_EOL; - echo "NO RDR:" . PHP_EOL; - echo var_dump($nordr) . PHP_EOL; - echo "DISABLED:" . PHP_EOL; - echo var_dump($disabled) . PHP_EOL; - echo "NO SYNC:" . PHP_EOL; - echo var_dump($nosync) . PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "NAT REFLECTION:".PHP_EOL; - echo var_dump($natreflection).PHP_EOL; - echo "ADD TO TOP:" . PHP_EOL; - echo var_dump($top) . PHP_EOL; - echo "NEXT RULE ID:" . PHP_EOL; - echo var_dump($next_rule_id) . PHP_EOL; - } - // INPUT VALIDATION/FORMATTING - $api_resp = array("status" => "bad request", "code" => 400); - // Check that our required array/interface values are valid - if (!is_string($interface)) { - $api_resp["return"] = 4006; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (!in_array($protocol, $allowed_prot)) { - $api_resp["return"] = 4007; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (isset($natreflection) and !in_array($natreflection, $allowed_nat_ref)) { - $api_resp["return"] = 4008; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (!is_ipaddrv4($localip) and !alias_in_use($localip)) { - $api_resp["return"] = 4009; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (!is_port_or_range($localport)) { - $api_resp["return"] = 4010; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - $rule_ent = array(); - // Check if rule is disabled - if ($disabled) { - $rule_ent["disabled"] = ""; - } - // Check if pfsync is disabled - if ($nosync) { - $rule_ent["nosync"] = ""; - } - // Check if RDR is disabled is disabled - if ($nordr) { - $rule_ent["nordr"] = ""; - } - // Check if user specified NAT reflection - if ($natreflection) { - $rule_ent["natreflection"] = $natreflection; - } - $rule_ent["interface"] = $interface; - $rule_ent["protocol"] = $protocol; - $rule_ent["source"] = array(); - $rule_ent["destination"] = array(); - $rule_ent["target"] = $localip; - $rule_ent["local-port"] = $localport; - $rule_ent["descr"] = $descr; - $rule_ent["associated-rule-id"] = "pass"; - $rule_ent["created"] = array("time" => time(), "username" => $user_created_msg); - $rule_ent["updated"] = $rule_ent["created"]; - // Check if our source and destination values are valid - foreach (array("source" => $src, "destination" => $dst) as $dir => $val) { - $dir_check = is_valid_rule_addr($val, $dir); - if (!$dir_check["valid"]) { - if ($dir === "source") { - $api_resp["return"] = 4011; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $api_resp["return"] = 4012; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $rule_ent = array_merge($rule_ent, $dir_check["data"]); - } - } - // Check if protocol calls for additional specifications - if (in_array($protocol, array("tcp", "udp", "tcp/udp"))) { - $port_req = true; - } - // Check our src and dst port values if ports are required - if ($port_req) { - foreach (array("source" => $srcport, "destination" => $dstport) as $dir => $val) { - $val = str_replace("-", ":", $val); - if (!is_port_or_range($val) and $val !== "any") { - if ($dir === "source") { - $api_resp["return"] = 4013; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $api_resp["return"] = 4014; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } elseif ($val !== "any") { - $rule_ent[$dir]["port"] = str_replace(":", "-", $val); - } - } - } - // Add our port forward - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added NAT rule via API"; // Add a change note - $config["nat"]["rule"][] = $rule_ent; // Write to our master config - sort_nat_rules($top, $next_rule_id); // Sort our nat rules - write_config(sprintf(gettext($change_note))); // Apply our configuration change - filter_configure(); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added port forward"; - $api_resp["data"] = $rule_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_nat_portforwards_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-nat-portforward-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['id'])) { - $rule_id = $client_params['id']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4015); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "RULE ID TO DELETE:" . PHP_EOL; - echo var_dump($rule_id) . PHP_EOL; - } - // Check that our rule ID exists - if (array_key_exists($rule_id, $config["nat"]["rule"])) { - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted NAT rule via API"; // Add a change note - $del_rule = $config["nat"]["rule"][$rule_id]; // Save the rule we are deleting - unset($config["nat"]["rule"][$rule_id]); // Remove rule from our config - sort_nat_rules(); // Sort our NAT rules - write_config(sprintf(gettext($change_note))); // Apply our configuration change - filter_configure(); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted port forward"; - $api_resp["data"] = $del_rule; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4016); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_virtualips() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-virtualipaddresses"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $vip_array = array(); // Init our virtual IP array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a virtual IP configuration - if (!empty($config["virtualip"]["vip"])) { - $vip_array = $config["virtualip"]["vip"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $vip_array = api_extended_search($vip_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $vip_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_virtualips_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-virtualipaddress-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['id'])) { - $vip_id = $client_params['id']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4017); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "VIRTUAL IP TO DELETE:" . PHP_EOL; - echo var_dump($vip_id) . PHP_EOL; - } - // Check that our rule ID exists - if (array_key_exists($vip_id, $config["virtualip"]["vip"])) { - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted virtual IP via API"; // Add a change note - $del_vip = $config["virtualip"]["vip"][$vip_id]; // Save the virtual IP we are deleting - bring_down_virtual_ip($del_vip, $vip_id); // Bring down our virtual IP and delete it from config - write_config(sprintf(gettext($change_note))); // Apply our configuration change - send_event("filter reload"); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted virtual IP"; - $api_resp["data"] = $del_vip; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4018); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_virtualips_add() { - # VARIABLES - global $err_lib, $g, $config, $tracker, $api_resp, $client_id, $client_params; - $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-virtualipaddress-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $allowed_vip_modes = array("ipalias", "carp", "proxyarp", "other"); // Save our allowed vip modes in array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - $api_resp = array("status" => "bad request", "code" => 400); - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['mode'])) { - $mode = strtolower($client_params['mode']); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4019); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['interface'])) { - $interface = $client_params['interface']; - $interface = get_pfsense_if_id($interface); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4020); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['subnet'])) { - $subnet = $client_params['subnet']; - // If a single IPv4 or IPv6, append the subnet mask for one address - if (is_ipaddrv4($subnet)) { - $subnet = $subnet."/32"; - } elseif (is_ipaddrv6($subnet)) { - $subnet = $subnet."/128"; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4021); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } else { - $descr = ""; - } - if (isset($client_params['noexpand'])) { - $noexpand = true; - } - if (!in_array($mode, $allowed_vip_modes)) { - $api_resp["return"] = 4023; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // CARP required attributes - if ($mode === "carp" and isset($client_params['vhid'])) { - $vhid = $client_params['vhid']; - } - if ($mode === "carp" and isset($client_params['advskew'])) { - $advskew = intval($client_params['advskew']); - } else { - $advskew = 0; // Default skew to 0 - } - if ($mode === "carp" and isset($client_params['advbase'])) { - $advbase = intval($client_params['advbase']); - } else { - $advbase = 1; // Default base to 0 - } - if ($mode === "carp" and isset($client_params['password'])) { - $password = $client_params['password']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4022); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "MODE:" . PHP_EOL; - echo var_dump($mode) . PHP_EOL; - echo "INTERFACE:" . PHP_EOL; - echo var_dump($interface) . PHP_EOL; - echo "SUBNET:" . PHP_EOL; - echo var_dump($subnet) . PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "DISABLE IP EXPANSION:" . PHP_EOL; - echo var_dump($noexpand) . PHP_EOL; - echo "CARP VHID" . PHP_EOL; - echo var_dump($vhid) . PHP_EOL; - echo "CARP ADVERTISEMENT BASE" . PHP_EOL; - echo var_dump($advbase) . PHP_EOL; - echo "CARP ADVERTISEMENT SKEW" . PHP_EOL; - echo var_dump($advskew) . PHP_EOL; - echo "CARP PASSWORD" . PHP_EOL; - echo var_dump(isset($password)) . PHP_EOL; - } - // INPUT VALIDATION/FORMATTING - $api_resp = array("status" => "bad request", "code" => 400); - // Check that our required array/interface values are valid - if (!is_string($interface)) { - $api_resp["return"] = 4024; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (!is_subnet($subnet)) { - $api_resp["return"] = 4025; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Split our subnet into an address and subnet mask - $subnet_split = explode("/", $subnet); - $subnet = $subnet_split[0]; - $subnet_bits = $subnet_split[1]; - // Check that our subnet is not used elsewhere - if (is_ip_in_use($subnet)) { - $api_resp["return"] = 4026; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if VHID was specified - if (isset($vhid)) { - if (vhid_exists($vhid)) { - $api_resp["return"] = 4027; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (1 > $vhid or $vhid > 255) { - $api_resp["return"] = 4028; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } elseif ($mode === "carp" and !isset($vhid)) { - $vhid = next_vhid(); // Pull our next available VHID - } - // Check if advertisement base was specified - if (isset($advbase)) { - if (1 > $advbase or $advbase > 254) { - $api_resp["return"] = 4029; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Check if advertisement skew was specified - if (isset($advskew)) { - if (0 > $advskew or $advskew > 254) { - $api_resp["return"] = 4030; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Initialize our virtual IP configuration array - if (!is_array($config["virtualip"]["vip"])) { - $config["virtualip"] = array("vip" => []); - } - // Populate our new virtual IP entry - $vip_ent = array(); - $vip_ent["mode"] = $mode; - $vip_ent["interface"] = $interface; - $vip_ent["type"] = "network"; - $vip_ent["subnet"] = $subnet; - $vip_ent["subnet_bits"] = $subnet_bits; - $vip_ent["descr"] = $descr; - // Values specific to CARP - if ($mode === "carp") { - $vip_ent["vhid"] = $vhid; - $vip_ent["advbase"] = $advbase; - $vip_ent["advskew"] = $advskew; - $vip_ent["password"] = $password; - $vip_ent["uniqid"] = uniqid(); - } - // Values specific to Proxy ARP and other - if (in_array($mode, array("proxyarp", "other")) and $noexpand) { - $vip_ent["noexpand"] = ""; - } - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added virtual IP via API"; // Add a change note - $config["virtualip"]["vip"][] = $vip_ent; // Write to our master config - write_config(sprintf(gettext($change_note))); // Apply our configuration change - apply_virtual_ip($vip_ent); // Apply our backend changes with our new configuration - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added virtual IP"; - $api_resp["data"] = $vip_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_rules() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-rules"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $rule_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["filter"]["rule"])) { - $rule_array = $config["filter"]["rule"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $rule_array = api_extended_search($rule_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $rule_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_rules_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-rules-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['id'])) { - $rule_id = $client_params['id']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4031); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "RULE ID TO DELETE:" . PHP_EOL; - echo var_dump($rule_id) . PHP_EOL; - } - // Check that our rule ID exists - if (array_key_exists($rule_id, $config["filter"]["rule"])) { - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted firewall rule via API"; // Add a change note - $del_rule = $config["filter"]["rule"][$rule_id]; // Save the rule we are deleting - unset($config["filter"]["rule"][$rule_id]); // Remove rule from our config - sort_firewall_rules(); // Sort our firewall rules - write_config(sprintf(gettext($change_note))); // Apply our configuration change - send_event("filter reload"); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted firewall rule"; - $api_resp["data"] = $del_rule; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4032); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_rules_add() { - # VARIABLES - global $err_lib, $g, $config, $tracker, $api_resp, $client_id, $client_params; - $user_created_msg = $_SESSION["Username"]."@".$_SERVER["REMOTE_ADDR"]." (API)"; // Save the username and ip of client - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-rules-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $allowed_rule_types = array("pass", "block", "reject"); // Array of allowed rule types - $allowed_ip_prot = array("inet", "inet6", "inet46"); // Array of allowed IP protocols - $next_rule_id = count($config["filter"]["rule"]); // Save our next rule ID - // Array of supported protocols - $allowed_prot = array( - "any", - "tcp", - "udp", - "tcp/udp", - "icmp", - "esp", - "ah", - "gre", - "ipv6", - "igmp", - "pim", - "ospf", - "carp", - "pfsync" - ); - // Array of allowed ICMP subtypes - $icmp_subtypes = array( - "althost", - "dataconv", - "echorep", - "echoreq", - "inforep", - "inforeq", - "ipv6-here", - "ipv6-where", - "maskrep", - "maskreq", - "mobredir", - "mobregrep", - "mobregreq", - "paramprob", - "photuris", - "redir", - "routeradv", - "routersol", - "skip", - "squench", - "timerep", - "timereq", - "timex", - "trace", - "unreach" - ); - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['type'])) { - $type = $client_params['type']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4033); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['interface'])) { - $interface = $client_params['interface']; - $interface = get_pfsense_if_id($interface); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4034); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ipprotocol'])) { - $ipprotocol = $client_params['ipprotocol']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4035); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['protocol'])) { - $protocol = $client_params['protocol']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4036); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - - } - if (isset($client_params['src'])) { - $src = $client_params['src']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4037); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['srcport'])) { - $srcport = $client_params['srcport']; - } - if (isset($client_params['dst'])) { - $dst = $client_params['dst']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4038); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['dstport'])) { - $dstport = $client_params['dstport']; - } - if (isset($client_params['icmptype'])) { - $icmp_type = $client_params['icmptype']; - if (!is_array($icmp_type)) { - $icmp_type = array($icmp_type); - } - } - if (isset($client_params['gateway'])) { - $gateway = $client_params['gateway']; - } - if (isset($client_params['disabled'])) { - $disabled = true; - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } - if (isset($client_params['log'])) { - $log = true; - } - if (isset($client_params['top'])) { - $top = "top"; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "TYPE:" . PHP_EOL; - echo var_dump($type) . PHP_EOL; - echo "INTERFACE:" . PHP_EOL; - echo var_dump($interface) . PHP_EOL; - echo "IP PROTOCOL:" . PHP_EOL; - echo var_dump($ipprotocol) . PHP_EOL; - echo "PROTOCOL:" . PHP_EOL; - echo var_dump($protocol) . PHP_EOL; - echo "ICMP SUBTYPES:" . PHP_EOL; - echo var_dump($icmp_type) . PHP_EOL; - echo "SOURCE:" . PHP_EOL; - echo var_dump($src) . PHP_EOL; - echo "SOURCE PORT:" . PHP_EOL; - echo var_dump($srcport) . PHP_EOL; - echo "DESTINATION:" . PHP_EOL; - echo var_dump($dst) . PHP_EOL; - echo "DESTINATION PORT:" . PHP_EOL; - echo var_dump($dstport) . PHP_EOL; - echo "GATEWAY:" . PHP_EOL; - echo var_dump($gateway) . PHP_EOL; - echo "DISABLED:" . PHP_EOL; - echo var_dump($disabled) . PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "LOG MATCHES:" . PHP_EOL; - echo var_dump($log) . PHP_EOL; - echo "ADD TO TOP:" . PHP_EOL; - echo var_dump($top) . PHP_EOL; - echo "NEXT RULE ID:" . PHP_EOL; - echo var_dump($next_rule_id) . PHP_EOL; - } - // INPUT VALIDATION/FORMATTING - $api_resp = array("status" => "bad request", "code" => 400); - // Check that our required array/interface values are valid - if (!in_array($type, $allowed_rule_types)) { - $input_err = true; - $api_resp["return"] = 4039; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } elseif (!is_string($interface)) { - $input_err = true; - $api_resp["return"] = 4040; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } elseif (!in_array($ipprotocol, $allowed_ip_prot)) { - $input_err = true; - $api_resp["return"] = 4041; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } elseif (!in_array($protocol, $allowed_prot)) { - $input_err = true; - $api_resp["return"] = 4042; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } elseif (isset($gateway) and !is_gateway($gateway)) { - $input_err = true; - $api_resp["return"] = 4043; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } - $rule_ent = array(); - // Check if rule is not disabled - if (!$disabled) { - $rule_ent["id"] = ""; - } - // Check if logging is enabled - if ($log) { - $rule_ent["log"] = ""; - } - // Check if gateway was specified - if (isset($gateway)) { - $rule_ent["gateway"] = $gateway; - } - $rule_ent["type"] = $type; - $rule_ent["interface"] = $interface; - $rule_ent["ipprotocol"] = $ipprotocol; - $rule_ent["source"] = array(); - $rule_ent["destination"] = array(); - $rule_ent["descr"] = $descr; - $rule_ent["created"] = array("time" => time(), "username" => $user_created_msg); - $rule_ent["updated"] = $rule_ent["created"]; - // Save our protocol if it is not 'any' - if ($protocol !== "any") { - $rule_ent["protocol"] = $protocol; - } - // Add logging to config if enabled - if ($log) { - $rule_ent["log"] = ""; - } - // Check if our source and destination values are valid - foreach (array("source" => $src, "destination" => $dst) as $dir => $val) { - $dir_check = is_valid_rule_addr($val, $dir); - if (!$dir_check["valid"] === true) { - $input_err = true; - if ($dir === "source") { - $api_resp["return"] = 4044; - } else { - $api_resp["return"] = 4045; - } - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } else { - $rule_ent = array_merge($rule_ent, $dir_check["data"]); - } - } - // Check if protocol calls for additional specifications - if (in_array($protocol, array("tcp", "udp", "tcp/udp"))) { - $port_req = true; - } elseif ($protocol === "icmp") { - // Check if user specified ICMP subtypes - if (!$input_err and is_array($icmp_type)) { - // Loop through each of our subtypes - foreach ($icmp_type as $ict) { - if (!in_array($ict, $icmp_subtypes)) { - $input_err = true; - $api_resp["return"] = 4046; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } - } - // Write our ICMP subtype config - $rule_ent["icmptype"] = implode(",", $icmp_type); - } - } - // Check our src and dst port values if ports are required - if (!$input_err and $port_req) { - if (!isset($srcport) or !isset($dstport)) { - $input_err = true; - $api_resp["return"] = 4047; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } - foreach (array("source" => $srcport, "destination" => $dstport) as $dir => $val) { - $val = str_replace("-", ":", $val); - if (!is_port_or_range($val) and $val !== "any") { - $input_err = true; - if ($dir === "source") { - $api_resp["return"] = 4048; - } else { - $api_resp["return"] = 4049; - } - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } elseif ($val !== "any") { - $rule_ent[$dir]["port"] = str_replace(":", "-", $val);; - } - } - } - // Return error if an error was found during input validation - if ($input_err) { - return $api_resp; - } - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added firewall rule via API"; // Add a change note - $config["filter"]["rule"][] = $rule_ent; // Write to our master config - sort_firewall_rules($top, $next_rule_id); // Sort our firewall rules - write_config(sprintf(gettext($change_note))); // Apply our configuration change - send_event("filter reload"); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added firewall rule"; - $api_resp["data"] = $rule_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_aliases() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-aliases"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $alias_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["aliases"]["alias"])) { - $alias_array = $config["aliases"]["alias"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $alias_array = api_extended_search($alias_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $alias_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_aliases_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-aliases-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['name'])) { - $name = $client_params['name']; - $name = sanitize_str($name); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4050); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "NAME:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - } - // Check that alias is not in use in our configuration - if (!alias_in_use($name)) { - // Loop through our current config and find the index ID for our alias to delete - $c_count = 0; // Init loop counter - foreach ($config["aliases"]["alias"] as $ce) { - // Check if this entry matches our requested value - if ($ce["name"] === $name) { - $del_index = $c_count; - break; - } - $c_count++; // Increase our counter - } - // Delete our alias if we found a match, - $del_conf = []; - if (is_numeric($del_index)) { - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted firewall alias via API"; // Add a change note - $del_conf = $config["aliases"]["alias"][$del_index]; // Save our alias config before deleting - unset($config["aliases"]["alias"][$del_index]); // Remove this alias from our configuration - $config["aliases"]["alias"] = array_values($config["aliases"]["alias"]); // Reindex array - write_config(sprintf(gettext($change_note))); // Apply our configuration change - send_event("filter reload"); // Ensure our firewall filter is reloaded - } - // Check if our alias is still in the configuration, if so return error response - foreach ($config["aliases"]["alias"] as $se) { - if ($se["name"] === $name) { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Return success if we did not find our deleted alias in configuration during loop - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "data" => $del_conf); - $api_resp["message"] = "Successfully deleted firewall alias"; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4051); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_aliases_delete_address() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-aliases-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $allowed_alias_types = array("host", "network", "port"); // Array of allowed alias types - $detail = array(); - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - $api_resp = array("status" => "bad request", "code" => 400); - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['name'])) { - $name = $client_params['name']; - $name = sanitize_str($name); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4050); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['address'])) { - $address = $client_params['address']; - // Convert string to array - if (!is_array($address)) { - $address = array($address); - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4052); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "NAME:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - echo "ALIAS VALUES TO DELETE:".PHP_EOL; - echo var_dump($address).PHP_EOL; - } - // Check that our input is valid - if (!is_string($name)) { - $type_err = true; - $api_resp["return"] = 4053; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } elseif (!is_array($address)) { - $type_err = true; - $api_resp["return"] = 4054; - $api_resp["message"] = $err_lib[$api_resp["return"]]; } - // Loop through our existing firewall entries and check for our requested alias - $c_count = 0; - foreach ($config["aliases"]["alias"] as $ce) { - if ($name === $ce["name"]) { - $alias_found = true; - $a_index = $c_count; - $curr_addr = explode(" ", $ce["address"]); - $curr_detail = explode("||", $ce["detail"]); - break; - } - $c_count++; // Increase our counter - } - // If we could not find an alias, return error - if ($alias_found !== true) { - $type_err = true; - $api_resp["return"] = 4055; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } - // Return bad request if error - if (isset($type_err)) { - return $api_resp; - } - // Loop through our existing configuration and remove alias address values on match - $r_count = 0; - foreach ($curr_addr as $re) { - if (in_array($re, $address)) { - unset($curr_addr[$r_count]); - unset($curr_detail[$r_count]); - } - $r_count++; // Increase our counter - } - // Add our alias - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted firewall alias address via API"; // Add a change note - $config["aliases"]["alias"][$a_index]["address"] = implode(" ", $curr_addr); - $config["aliases"]["alias"][$a_index]["detail"] = implode("||", $curr_detail); - write_config(sprintf(gettext($change_note))); // Apply our configuration change - send_event("filter reload"); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted firewall alias address"; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_aliases_modify() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-aliases-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $allowed_alias_types = array("host", "network", "port"); // Array of allowed alias types - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['name'])) { - $name = $client_params['name']; - $name = sanitize_str($name); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4050); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params["new_name"])) { - $new_name = $client_params['new_name']; - $new_name = sanitize_str($new_name); - } - if (isset($client_params['type'])) { - $type = $client_params['type']; - $type = trim($type); - } else { - $type = alias_get_type($name); - } - if (isset($client_params['address'])) { - $address = $client_params['address']; - // Convert string to array - if (!is_array($address)) { - $address = array($address); - } - } - if (isset($client_params['descr'])) { - $descr = strval($client_params['descr']); - $descr = str_replace(PHP_EOL, "", $descr); - } - if (isset($client_params['detail'])) { - $detail = $client_params['detail']; - // Convert string to array - if (!is_array($detail)) { - $detail = array($detail); - } - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "NAME:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - echo "NEW NAME:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - echo "TYPE:" . PHP_EOL; - echo var_dump($type) . PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "ALIAS VALUES:".PHP_EOL; - echo var_dump($address).PHP_EOL; - echo "ALIAS VALUE DESCRIPTIONS:" . PHP_EOL; - echo var_dump($detail) . PHP_EOL; - } - // INPUT VALIDATION - if (!is_alias($name)) { - $input_err = true; - $api_resp["return"] = 4055; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } elseif (isset($new_name) and is_alias($new_name)) { - $input_err = true; - $api_resp["return"] = 4056; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } elseif (isset($type) and !in_array($type, $allowed_alias_types)) { - $input_err = true; - $api_resp["return"] = 4057; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - } - if (!$input_err and isset($address)) { - foreach ($address as $nae) { - if ($type === "host") { - if (!is_ipaddr($nae) and !is_hostname($nae)) { - $input_err = true; - $api_resp["return"] = 4058; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - break; - } - } - if ($type === "network") { - if (!is_subnet($nae) and !is_hostname($nae)) { - $input_err = true; - $api_resp["return"] = 4059; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - break; - } - } - if ($type === "port") { - if (!is_port_or_range($nae)) { - $input_err = true; - $api_resp["return"] = 4060; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - break; - } - } - } - } - // If we encountered an error, return error response - if ($input_err) { - $api_resp["code"] = 400; - $api_resp["status"] = "bad request"; - return $api_resp; - } - // Save our alias ID - $c_count = 0; - foreach ($config["aliases"]["alias"] as $ca) { - if ($name === $ca["name"]) { - $a_index = $c_count; - } - $c_count++; - } - // Make our alias change - $curr_ent = $config["aliases"]["alias"][$a_index]; // Save our current alias entry - if (isset($new_name)) { - $curr_ent["name"] = $new_name; - update_alias_name($new_name, $name); // Update our alias name - } - if (isset($type)) { - $curr_ent["type"] = $type; - } - if (isset($descr)) { - $curr_ent["descr"] = $descr; - } - if (isset($address)) { - $curr_ent["address"] = implode(" ", $address); - } - if (isset($detail)) { - $curr_ent["detail"] = implode("||", $detail); - } - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Modified firewall alias address via API"; // Add a change note - $config["aliases"]["alias"][$a_index] = $curr_ent; // Write to our master config - write_config(sprintf(gettext($change_note))); // Apply our configuration change - send_event("filter reload"); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully modified firewall alias"; - $api_resp["data"] = $config["aliases"]["alias"][$a_index]; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_aliases_add() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-aliases-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $allowed_alias_types = array("host", "network", "port"); // Array of allowed alias types - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - $api_resp = array("status" => "bad request", "code" => 400); - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['name'])) { - $name = $client_params['name']; - $name = sanitize_str($name); - } else { - $api_resp["return"] = 4050; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['type'])) { - $type = $client_params['type']; - $type = trim($type); - } else { - $api_resp["return"] = 4061; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['address'])) { - $address = $client_params['address']; - // Convert string to array - if (!is_array($address)) { - $address = array($address); - } - } else { - $api_resp["return"] = 4052; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } - if (isset($client_params['detail'])) { - $detail = $client_params['detail']; - // Convert string to array - if (!is_array($detail)) { - $detail = array($detail); - } - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "NAME:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - echo "TYPE:" . PHP_EOL; - echo var_dump($type) . PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "ALIAS VALUES:".PHP_EOL; - echo var_dump($address).PHP_EOL; - echo "ALIAS VALUE DESCRIPTIONS:" . PHP_EOL; - echo var_dump($detail) . PHP_EOL; - } - // Check that our input is valid - if (!is_string($name)) { - $api_resp["return"] = 4053; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (!is_string($type)) { - $api_resp["return"] = 4062; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (!in_array($type, $allowed_alias_types)) { - $api_resp["return"] = 4057; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (isset($descr) and !is_string($descr)) { - $api_resp["return"] = 4063; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (!is_array($address)) { - $api_resp["return"] = 4054; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (isset($detail) and !is_array($detail)) { - $api_resp["return"] = 4063; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (!isset($type_err)) { - // Loop through our arrays and ensure the values are valid - $a_count = 0; // Define a loop counter - foreach ($address as $ae) { - // Conditions for alias type 'port' - if ($type === "port") { - // Check that our value is numeric - if (is_numeric($ae)) { - if (1 <= intval($ae) and intval($ae) <= 65535) { - $address[$a_count] = strval($ae); - } else { - $api_resp["return"] = 4065; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 4066; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Conditionals for alias type 'network' - if ($type === "network") { - // Check that values are strings - if (is_string($ae)) { - // Check that string is a network CIDR - if (strpos($ae, "/")) { - $net_ip = explode("/", $ae)[0]; // Save our network IP - $bit_mask = explode("/", $ae)[1]; // Save our subnet bit mask - // Check if our IP is IPv4 - if (is_ipaddrv4($net_ip)) { - $max_bits = 32; // Assign our maximum IPv4 bitmask - } elseif (is_ipaddrv6($net_ip)) { - $max_bits = 128; // Assign our maximum IPv4 bitmask - } else { - $api_resp["return"] = 4067; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our bitmask is numeric and in range - if (is_numeric($bit_mask)) { - if (1 <= intval($bit_mask) and intval($bit_mask) <= $max_bits) { - continue; - } else { - $api_resp["return"] = 4068; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 4069; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 4069; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 4070; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Conditions for alias type 'host' - if ($type === "host") { - // Check that values are strings - if (is_string($ae)) { - $address[$a_count] = sanitize_str($ae); - } else { - $api_resp["return"] = 4070; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Increase our counter - $a_count++; - } - // Check each of our alias details - foreach ($detail as $de) { - if (!is_string($de)) { - $api_resp["return"] = 4071; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - // Check our existing aliases - if (is_array($config["aliases"])) { - $c_count = 0; - // Loop through each alias and see if alias already exists - foreach ($config["aliases"]["alias"] as $ce) { - if ($ce["name"] === $name) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4056); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - // Add our alias - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added firewall alias via API"; // Add a change note - $alias_ent = array(); // Init our array - $alias_ent["name"] = $name; // Save our alias name - $alias_ent["type"] = $type; // Save our type - $alias_ent["descr"] = $descr; // Save our description - $alias_ent["address"] = implode(" ", $address); // Join array in to space seperated string - $alias_ent["detail"] = implode("||", $detail); // Join array in to || seperated string - $config["aliases"] = !is_array($config["aliases"]) ? array("alias" => []) : $config["aliases"]; - $config["aliases"]["alias"][] = $alias_ent; // Write our configuration change - write_config(sprintf(gettext($change_note))); // Apply our configuration change - send_event("filter reload"); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - foreach ($config["aliases"]["alias"] as $se) { - if ($se["name"] === $name) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added firewall alias"; - $api_resp["data"] = $se; - return $api_resp; - } - } - // Return error response if our loop did not find a match - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_aliases_add_address() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-firewall-aliases-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $allowed_alias_types = array("host", "network", "port"); // Array of allowed alias types - $detail = array(); - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - $api_resp = array("status" => "bad request", "code" => 400); - if (isset($client_params['name'])) { - $name = $client_params['name']; - $name = sanitize_str($name); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4050); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['address'])) { - $address = $client_params['address']; - // Convert string to array - if (!is_array($address)) { - $address = array($address); - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4052); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['detail'])) { - $detail = $client_params['detail']; - // Convert string to array - if (!is_array($detail)) { - $detail = array($detail); - } - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "NAME:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - echo "ALIAS VALUES:".PHP_EOL; - echo var_dump($address).PHP_EOL; - echo "ALIAS VALUE DESCRIPTIONS:" . PHP_EOL; - echo var_dump($detail) . PHP_EOL; - } - // Check that our input is valid - if (!is_string($name)) { - $api_resp["return"] = 4053; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (!is_array($address)) { - $api_resp["return"] = 4054; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (isset($detail) and !is_array($detail)) { - $api_resp["return"] = 4064; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Loop through our existing firewall entries and check for our requested alias - $c_count = 0; - foreach ($config["aliases"]["alias"] as $ce) { - if ($name === $ce["name"]) { - $a_index = $c_count; - $type = $ce["type"]; - $curr_addr = explode(" ", $ce["address"]); - $curr_detail = explode("||", $ce["detail"]); - break; - } - $c_count++; // Increase our counter - } - // If we could not find an alias, return error - if (!isset($type)) { - $api_resp["return"] = 4055; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (!isset($type_err)) { - // Loop through our arrays and ensure the values are valid - $a_count = 0; // Define a loop counter - foreach ($address as $ae) { - // Conditions for alias type 'port' - if ($type === "port") { - // Check that our value is numeric - if (is_numeric($ae)) { - if (1 <= intval($ae) and intval($ae) <= 65535) { - $address[$a_count] = strval($ae); - } else { - $api_resp["return"] = 4065; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 4066; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Conditionals for alias type 'network' - if ($type === "network") { - // Check that values are strings - if (is_string($ae)) { - // Check that string is a network CIDR - if (strpos($ae, "/")) { - $net_ip = explode("/", $ae)[0]; // Save our network IP - $bit_mask = explode("/", $ae)[1]; // Save our subnet bit mask - // Check if our IP is IPv4 - if (is_ipaddrv4($net_ip)) { - $max_bits = 32; // Assign our maximum IPv4 bitmask - } elseif (is_ipaddrv6($net_ip)) { - $max_bits = 128; // Assign our maximum IPv4 bitmask - } else { - $api_resp["return"] = 4067; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our bitmask is numeric and in range - if (is_numeric($bit_mask)) { - if (1 <= intval($bit_mask) and intval($bit_mask) <= $max_bits) { - continue; - } else { - $api_resp["return"] = 4068; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 4069; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 4069; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 4070; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Conditions for alias type 'host' - if ($type === "host") { - // Check that values are strings - if (is_string($ae)) { - $address[$a_count] = sanitize_str($ae); - } else { - $api_resp["return"] = 4070; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Increase our counter - $a_count++; - } - // Check each of our alias details - foreach ($detail as $de) { - if (!is_string($de)) { - $api_resp["return"] = 4071; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - // Add our alias - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added firewall alias address via API"; // Add a change note - $new_addr = array_merge($curr_addr, $address); - $new_detail = array_merge($curr_detail, $detail); - $config["aliases"]["alias"][$a_index]["address"] = implode(" ", $new_addr); - $config["aliases"]["alias"][$a_index]["detail"] = implode("||", $new_detail); - write_config(sprintf(gettext($change_note))); // Apply our configuration change - send_event("filter reload"); // Ensure our firewall filter is reloaded - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added firewall alias address"; - $api_resp["data"] = $config["aliases"]["alias"][$a_index]; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_states() { - # VARIABLES - global $err_lib, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-diagnostics-statessummary"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - $state_array = sort_state_table(); - if (isset($client_params['search'])) { - $search = $client_params['search']; - $state_array = api_extended_search($state_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $state_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_states_size() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-diagnostics-statessummary"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check our maximum state table size - if (isset($config["system"]["maximumstates"])) { - $size_array["maximumstates"] = intval($config["system"]["maximumstates"]); - } else { - $size_array["maximumstates"] = intval(pfsense_default_state_size()); - } - // Check our current state table size - $size_array["currentstates"] = count(sort_state_table()); - // Check our default state table size - $size_array["defaultmaximumstates"] = intval(pfsense_default_state_size()); - // Allow extended search - if (isset($client_params['search'])) { - $search = $client_params['search']; - $size_array = api_extended_search($size_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $size_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_firewall_states_size_modify() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params, $client_id; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-advanced-firewall"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'POST') { - // Check if we set a maximumstates size - if (isset($client_params["maximumstates"])) { - $maxsize = $client_params["maximumstates"]; - // Check that our state size is valid - if (is_numeric($maxsize) and (10 <= intval($maxsize))) { - $config["system"]["maximumstates"] = $maxsize; - } elseif ($maxsize === "default") { - unset($config["system"]["maximumstates"]); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4073); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 4072); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Write our state table size - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Modified firewall state table size via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - filter_configure(); // Update our firewall filter - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully modified firewall state table size"; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_config() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-diagnostics-backup-restore", "page-diagnostics-command"); // Allowed privs - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $config_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config)) { - $config_array = $config; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $config_array = api_extended_search($config_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $config_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_api() { - # VARIABLES - global $err_lib, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $api_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - $api_array = get_api_configuration()[1]; - if (isset($client_params['search'])) { - $search = $client_params['search']; - $api_array = api_extended_search($api_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $api_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_version() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-dashboard-widgets", "page-diagnostics-command"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $ver_path = "/etc/version"; // Assign the path to our version file - $ver_patch_path = "/etc/version.patch"; // Assign the path to our version patch file - $ver_bt_path = "/etc/version.buildtime"; // Assign the path to our version build time file - $ver_lc_path = "/etc/version.lastcommit"; // Assign the path to our version last commit file - $ver_data = array(); // Init an empty array for our version data - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that our files exist, if so read the files. Otherwise return error - if (file_exists($ver_path)) { - $ver_file = fopen($ver_path, "r"); // Open our file - $ver = str_replace(PHP_EOL, "", fread($ver_file, filesize($ver_path))); // Save our version data - $ver_data["version"] = $ver; // Save to array - } else { - $ver_fail = false; // Save a track indicating our read failed - } - if (file_exists($ver_patch_path)) { - $ver_patch_file = fopen($ver_patch_path, "r"); // Open our file - $ver_patch = str_replace(PHP_EOL, "", fread($ver_patch_file, filesize($ver_patch_path))); // Save patch - $ver_data["patch"] = $ver_patch; // Save to array - } else { - $ver_fail = false; // Save a track indicating our read failed - } - if (file_exists($ver_bt_path)) { - $ver_bt_file = fopen($ver_bt_path, "r"); // Open our file - $ver_bt = str_replace(PHP_EOL, "", fread($ver_bt_file, filesize($ver_bt_path))); // Save bt data - $ver_data["buildtime"] = $ver_bt; // Save to array - } else { - $ver_fail = false; // Save a track indicating our read failed - } - if (file_exists($ver_lc_path)) { - $ver_lc_file = fopen($ver_lc_path, "r"); // Open our file - $ver_lc = str_replace(PHP_EOL, "", fread($ver_lc_file, filesize($ver_lc_path))); // Save bt data - $ver_data["lastcommit"] = $ver_lc; // Save to array - } else { - $ver_fail = false; // Save a track indicating our read failed - } - // Check that our version read did not fail - if ($ver_fail !== false) { - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $ver_data); - return $api_resp; - } else { - $api_resp = array("status" => "not found", "code" => 404, "return" => 8); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_hostname() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system"); // Allowed privs - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $host_array = array("hostname" => "", "domain" => ""); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a hostname configuration - if (!empty($config["system"]["hostname"])) { - $host_array["hostname"] = $config["system"]["hostname"]; - } - // Check that we have a domain configuration - if (!empty($config["system"]["domain"])) { - $host_array["domain"] = $config["system"]["domain"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $host_array = api_extended_search($host_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $host_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_dns_modify() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params, $client_id; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $dns_ent = []; - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['dnsserver'])) { - $dns_servers = $client_params['dnsserver']; - // If value is not an array, convert it - if (!is_array($dns_servers)) { - $dns_servers = array($dns_servers); - } - // Loop through our DNS servers and check that entry is valid - foreach ($dns_servers as $ds) { - // Check if our DNS server is valid - if (!is_ipaddrv4($ds) and !is_ipaddrv6($ds)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 1007); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Add our system DNS values to config and track the change - $config["system"]["dnsserver"] = $dns_servers; - $dns_ent["dnsserver"] = $dns_servers; - } - if ($client_params['dnsallowoverride'] === true) { - $config["system"]["dnsallowoverride"] = ""; - $dns_ent["dnsallowoverride"] = $client_params['dnsallowoverride']; - } elseif ($client_params['dnsallowoverride'] === false) { - $dns_ent["dnsallowoverride"] = $client_params['dnsallowoverride']; - unset($config["system"]["dnsallowoverride"]); - } - if ($client_params['dnslocalhost'] === true) { - $config["system"]["dnslocalhost"] = ""; - $dns_ent["dnslocalhost"] = $client_params['dnslocalhost']; - } elseif ($client_params['dnslocalhost'] === false) { - $dns_ent["dnslocalhost"] = $client_params['dnslocalhost']; - unset($config["system"]["dnslocalhost"]); - } - // Write our new hostname - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Modified system DNS servers via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - // Update a slew of backend services - system_resolvconf_generate(); - if (isset($config['dnsmasq']['enable'])) { - services_dnsmasq_configure(); - } elseif (isset($config['unbound']['enable'])) { - services_unbound_configure(); - } - send_event("service reload dns"); - filter_configure(); - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully modified system DNS servers"; - $api_resp["data"] = $dns_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_dns_delete_servers() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params, $client_id; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $del_ent = []; - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['dnsserver'])) { - $del_server = $client_params['dnsserver']; - $curr_servers = $config["system"]["dnsserver"]; - $del_server = (!is_array($del_server)) ? array($del_server) : $del_server; - foreach ($del_server as $ds) { - // Ensure our config is array - if (!is_array($curr_servers)) { - $curr_servers = array($config["system"]["dnsserver"]); - } - // Loop through each server and check for matches, delete on match - foreach ($curr_servers as $id => $cs) { - if ($ds === $cs) { - $del_ent[] = $ds; - unset($config["system"]["dnsserver"][$id]); - } - } - } - } - // Write our new hostname - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted system DNS servers via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - // Update a slew of backend services - system_resolvconf_generate(); - if (isset($config['dnsmasq']['enable'])) { - services_dnsmasq_configure(); - } elseif (isset($config['unbound']['enable'])) { - services_unbound_configure(); - } - send_event("service reload dns"); - filter_configure(); - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted system DNS servers"; - $api_resp["data"] = $del_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_hostname_modify() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params, $client_id; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['hostname'])) { - $hostname = $client_params['hostname']; - $hostname = trim($hostname); - // Check if our hostname is valid - if (!is_hostname($hostname) or !is_unqualified_hostname($hostname)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 1000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $config["system"]["hostname"] = $hostname; - } - } - if (isset($client_params['domain'])) { - $domain = $client_params['domain']; - $domain = trim($domain); - // Check if our hostname is valid - if (!is_domain($domain)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 1001); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $config["system"]["domain"] = $domain; - } - } - // Write our new hostname - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Modified system hostname via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - // Update a slew of backend services - if (isset($hostname) or isset($domain)) { - system_hostname_configure(); - system_hosts_generate(); - system_resolvconf_generate(); - if (isset($config['dnsmasq']['enable'])) { - services_dnsmasq_configure(); - } elseif (isset($config['unbound']['enable'])) { - services_unbound_configure(); - } - filter_configure(); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully modified system hostname"; - $api_resp["data"] = array("hostname" => $config["system"]["hostname"], "domain" => $config["system"]["domain"]); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_dns() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system"); // Allowed privs - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $dns_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["system"])) { - $dns_keys = ["dnsserver", "dnsallowoverride", "dnslocalhost"]; - foreach ($config["system"] as $key => $sv) { - if (in_array($key, $dns_keys)) { - $dns_array[$key] = $sv; - } - } - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $dns_array = api_extended_search($dns_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $dns_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_certificates() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-certmanager"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $crt_array = array(); // Init our array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a virtual IP configuration - if (!empty($config["cert"])) { - $crt_array = $config["cert"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $crt_array = api_extended_search($crt_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $crt_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_certificates_add() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params, $client_id; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-certmanager"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $cert_ent = []; // Init our configuration array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 1002); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['method'])) { - $method = $client_params['method']; - // Check what method was picked - if ($method === "import") { - $cert = base64_decode($client_params["cert"]); - $key = base64_decode($client_params["key"]); - $active = $client_params["active"]; - // Check if our certificate and key are valid - if (!strstr($cert, "BEGIN CERTIFICATE") || !strstr($cert, "END CERTIFICATE")) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 1003); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (cert_get_publickey($cert, false) != cert_get_publickey($key, false, 'prv')) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 1004); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Populate our configuration array - $cert_ent["refid"] = uniqid(); - $cert_ent["type"] = "server"; - $cert_ent["descr"] = $descr; - cert_import($cert_ent, $cert, $key); - // If user requests this cert to be active, configure it as the wc cert - if ($active === true) { - $config["system"]["webgui"]["ssl-certref"] = $cert_ent["refid"]; - } - } - } - // Write our new certificate - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added certificate via API"; // Add a change note - $config["cert"][] = $cert_ent; // Add our configuration - write_config(sprintf(gettext($change_note))); // Apply our configuration change - // Restart our webgui if user set this cert as active - if ($active === true) { - restart_webconfigurator(); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added system certificate"; - $api_resp["data"] = $cert_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_certificates_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params, $client_id; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-certmanager"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $cert_del = []; // Init our configuration array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['refid'])) { - $refid = $client_params['refid']; - // Loop through our certificates and find a match - foreach ($config["cert"] as $i => $c) { - if ($c["refid"] === $refid) { - $del_id = $i; - break; - } - } - } elseif (isset($client_params['descr'])) { - $descr = $client_params['descr']; - // Loop through our certificates and find a match - foreach ($config["cert"] as $i => $c) { - if ($c["descr"] === $descr) { - $del_id = $i; - break; - } - } - } elseif (isset($client_params['id'])) { - $del_id = $client_params['id']; - } - // Write our new certificate - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted certificate via API"; // Add a change note - // Check if our del_id is in our array - if (array_key_exists($del_id, $config["cert"])) { - $cert_del = $config["cert"][$del_id]; // Save our cert we are deleting - // Check if our certificate is currently in use - if ($config["system"]["webgui"]["ssl-certref"] !== $cert_del["refid"]) { - unset($config["cert"][$del_id]); // Delete our cert - write_config(sprintf(gettext($change_note))); // Apply our configuration change - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 1005); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted system certificate"; - $api_resp["data"] = $cert_del; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_arp() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-diagnostics-arptable"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - $arp_data = get_arp_table(); - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $arp_data); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_system_arp_delete() { - # VARIABLES - global $err_lib, $api_resp, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-diagnostics-arptable"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $arp_cmd = "arp -d "; // Assign the command which deletes our ARP table value - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['ip'])) { - $ip = $client_params['ip']; - $ip = trim($ip); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 1006); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - $del_ent = get_arp_entry("ip", $ip); // Save our deleted ARP config - exec($arp_cmd.$ip, $arp_del); // Delete our ARP address - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted system ARP entry"; - $api_resp["data"] = $del_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users() { - # VARIABLES - global $err_lib, $config, $userindex, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-usermanager"); // Array of privileges allowing this action - $userindex = index_users(); // Index our users - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Save input data - if (isset($client_params['search'])) { - $search = $client_params['search']; - } - // Input validation - $base_data = $config["system"]["user"]; - $search_failed = false; - // If client requests an extended search - if (isset($search)) { - $base_data = api_extended_search($base_data, $search); - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "SEARCH ATTRIBUTES:" . PHP_EOL; - echo var_dump($search) . PHP_EOL; - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $base_data); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_delete() { - # VARIABLES - global $err_lib, $config, $userindex, $api_resp, $client_id, $client_params; - $logging_level = LOG_WARNING; // Set our log level - $logging_prefix = gettext("Local User Database"); // Set our logging prefix - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-usermanager"); // Array of privileges allowing this action - $userindex = index_users(); // Index our users - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $del_user = []; // Init our deleted user config array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['username'])) { - $username = $client_params['username']; - $username = trim($username); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our user already exists, if not exit on non-zero - if (!array_key_exists($username, $userindex)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5001); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "USERNAME:" . PHP_EOL; - echo var_dump($username) . PHP_EOL; - } - // Delete our user - $_SESSION["Username"] = $client_id; // Save our username to session data for logging - $change_note = " Deleted user `" . $username . "' via API"; // Add a change note - $index_id = $userindex[$username]; // Save our user's index ID number - local_user_del($config["system"]["user"][$index_id]); // Delete our user on the backend - $del_user = $config['system']['user'][$index_id]; // Save our deleted user config - unset($config['system']['user'][$index_id]); // Unset our user from config - $config['system']['user'] = array_values($config['system']['user']); // Reindex our users - write_config(sprintf(gettext($change_note))); // Write our new config - //--Check that our changes were successful----------- - $userindex = index_users(); // Update our user index - if (!array_key_exists($username, $userindex)) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted user"; - $api_resp["data"] = $del_user; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_delete_privs() { - # VARIABLES - global $err_lib, $config, $priv_list, $userindex, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set if function requires read only access - $userindex = index_users(); // Index our users - $req_privs = array("page-all", "page-system-usermanager-addprivs"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['username'])) { - $username = $client_params['username']; - $username = trim($username); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['priv'])) { - $del_priv = $client_params['priv']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5004); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our user already exists, if so exit on non-zero - $user_config =& getUserEntry($username); - $user_privs = get_user_privileges($user_config); - if (!array_key_exists("uid", $user_config)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5001); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Ensure our new priv is array, if it is a string create an array containing the string - if (is_string($del_priv)) { - $del_priv = array($del_priv); - } - if (is_array($del_priv)) { - // Loop through our new priv list and check that the privs are valid - foreach ($del_priv as $dp) { - if (!array_key_exists($dp, $priv_list)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5006); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (in_array($dp, $user_config["priv"])) { - $user_config["priv"] = \array_diff($user_config["priv"], array($dp)); - } - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5005); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "USERNAME:" . PHP_EOL; - echo var_dump($username) . PHP_EOL; - echo "USER EXISTING PRIVILEGES:" . PHP_EOL; - echo var_dump($user_privs) . PHP_EOL; - echo "USER NEW PRIVILEGES:" . PHP_EOL; - echo var_dump($user_config["priv"]) . PHP_EOL; - } - // Delete privilege - $_SESSION["Username"] = $client_id; // Save our session username for logging purposes - $change_note = " Deleted privileges for user `" . $username . "' via API"; // Add a change note - $user_id = $userindex[$username]; // Save our user's array index ID - local_user_set($user_config); // Set user backend parameters - $config["system"]["user"][$user_id] = $user_config; // Add our new config - write_config(sprintf(gettext($change_note))); // Write our new config - // Check our updated config to ensure our change was successful - parse_config(true); // Update our entire config - $userindex = index_users(); // Update our user index - $user_config =& getUserEntry($username); // Update our user config - $user_privs = get_user_privileges($user_config); // Update our user privs - // Check if our output is expected - if ($user_config["priv"] === $user_privs) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted user privilege"; - $api_resp["data"] = $del_priv; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_delete_groups() { - # VARIABLES - global $err_lib, $userindex, $groupindex, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-usermanager-addprivs", "page-system-groupmanager-addprivs"); // Allowed privs - $userindex = index_users(); // Index our users - $groupindex = index_groups(); // Index our groups - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['username'])) { - $username = $client_params['username']; - $username = trim($username); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['group'])) { - $del_groups = $client_params['group']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5007); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our user already exists, if so exit on non-zero - if (!array_key_exists($username, $userindex)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5001); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Ensure our new priv is array, if it is a string create an array containing the string - if (is_string($del_groups)) { - $del_groups = array($del_groups); - } - if (is_array($del_groups)) { - // Get our current user's groups - $user_config = getUserEntry($username); - $user_groups = local_user_get_groups($user_config, true); - // Loop through our del group list and ensure it is valid - foreach ($del_groups as $dg) { - if (!array_key_exists($dg, $groupindex)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5008); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (in_array($dg, $user_groups)) { - $new_groups = \array_diff($user_groups, array($dg)); - } - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5009); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "USERNAME:" . PHP_EOL; - echo var_dump($username) . PHP_EOL; - echo "USER GROUPS:" . PHP_EOL; - echo var_dump($user_groups) . PHP_EOL; - echo "USER DELETE GROUPS:" . PHP_EOL; - echo var_dump($del_groups) . PHP_EOL; - } - // Delete our group memberships - $_SESSION["Username"] = $client_id; // Save our session username for logging purposes - $change_note = " Deleted group membership for user `" . $username . "' via API"; // Add a change note - $user_id = $userindex[$username]; // Save our user's array index ID - local_user_set_groups($user_config, $new_groups); // Set our user's groups - local_user_set($user_config); // Reset our user - write_config(sprintf(gettext($change_note))); // Write our config - // Update our current user's groups - $user_config = getUserEntry($username); - $user_groups = local_user_get_groups($user_config, true); - if (array_diff($del_groups, $user_groups)) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted group membership"; - $api_resp["data"] = $del_groups; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_modify() { - # VARIABLES - global $err_lib, $userindex, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-usermanager"); // Array of privileges allowing this action - $userindex = index_users(); // Index our users - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['username'])) { - $username = $client_params['username']; - $username = trim($username); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['password'])) { - $password = $client_params['password']; - $password = trim($password); - } - if (array_key_exists("disabled", $client_params)) { - $disabled = true; - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - $descr = trim($descr); - } - if (isset($client_params['expires'])) { - $expires = $client_params['expires']; - $expires = trim($expires); - //$expires = new DateTime($expires); - } - if (isset($client_params['authorizedkeys'])) { - $authorizedkeys = $client_params['authorizedkeys']; - $authorizedkeys = trim($authorizedkeys); - } - if (isset($client_params['ipsecpsk'])) { - $ipsecpsk = $client_params['ipsecpsk']; - $ipsecpsk = trim($ipsecpsk); - } - // Check that our user already exists - $user_config =& getUserEntry($username); - if (!array_key_exists("uid", $user_config)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5001); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "USERNAME:" . PHP_EOL; - echo var_dump($username) . PHP_EOL; - echo "PASSWORD:" . PHP_EOL; - echo var_dump(isset($password)) . PHP_EOL; - echo "DISABLE:" . PHP_EOL; - echo var_dump($disabled) . PHP_EOL; - echo "FULL NAME:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "EXPIRES:".PHP_EOL; - echo var_dump($expires).PHP_EOL; - echo "AUTH KEY:" . PHP_EOL; - echo var_dump($authorizedkeys) . PHP_EOL; - echo "IPSEC KEY:" . PHP_EOL; - echo var_dump($ipsecpsk) . PHP_EOL; - } - // Modify our user - $_SESSION["Username"] = $client_id; // Save our username to session for logging - $change_note = " Modified user `" . $username . "' via API"; // Add a change note - $prev_config = $user_config; // Save a copy of our previous config for comparison - $user_config["scope"] = "user"; // Set our new user's system scope - // Check for our optional parameters - if (!empty(($password))) { - local_user_set_password($user_config, $password); // Set our new user's password - } - if (!empty($descr)) { - $user_config["descr"] = $descr; // Update our user's full name - } - if (!empty($expires)) { - $user_config["expires"] = $expires; // Update our user's expiration date - } - if (!empty($authorizedkeys)) { - $user_config["authorizedkeys"] = $authorizedkeys; // Update our user's authorized keys - } - if (!empty($ipsecpsk)) { - $user_config["ipsecpsk"] = $ipsecpsk; // Update our user's IPsec pre-shared key - } - if ($disabled === true) { - $user_config["disabled"] = ""; // Update our user's disabled value if not false - } else { - unset($user_config["disabled"]); // Unset our disabled value if not requested - } - // Update our user's configuration to our user list, then set our configuration and write config - local_user_set($user_config); - write_config(sprintf(gettext($change_note))); - // Check our updated config to ensure our change was successful - unset($user_config); - $userindex = index_users(); // Update our user index - $user_config =& getUserEntry($username); // Update our user config - if ($user_config !== $prev_config) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully modified user"; - $api_resp["data"] = $user_config; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_add() { - # VARIABLES - global $err_lib, $config, $userindex, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-usermanager"); // Array of privileges allowing this action - $userindex = index_users(); // Index our users - $uid = $config["system"]["nextuid"]; // Save our next UID - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['username'])) { - $username = $client_params['username']; - $username = trim($username); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['password'])) { - $password = $client_params['password']; - $password = trim($password); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5003); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if ($client_params["disabled"] === true) { - $disabled = true; - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - $descr = trim($descr); - } - if (isset($client_params['expires'])) { - $expires = $client_params['expires']; - $expires = trim($expires); - $expires = new DateTime($expires); - } - if (isset($client_params['authorizedkeys'])) { - $authorizedkeys = $client_params['authorizedkeys']; - $authorizedkeys = trim($authorizedkeys); - } - if (isset($client_params['ipsecpsk'])) { - $ipsecpsk = $client_params['ipsecpsk']; - $ipsecpsk = trim($ipsecpsk); - } - - // Check if our user already exists, if so exit on non-zero - $user_config =& getUserEntry($username); - if (array_key_exists("uid", $user_config)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5002); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "USERNAME:" . PHP_EOL; - echo var_dump($username) . PHP_EOL; - echo "DISABLE:" . PHP_EOL; - echo var_dump($disabled) . PHP_EOL; - echo "FULL NAME:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "EXPIRES:".PHP_EOL; - echo var_dump($expires).PHP_EOL; - echo "AUTH KEY:" . PHP_EOL; - echo var_dump($authorizedkeys) . PHP_EOL; - echo "IPSEC KEY:" . PHP_EOL; - echo var_dump($ipsecpsk) . PHP_EOL; - } - // Add our user - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added user `" . $username . "' via API"; // Add a change note - local_user_set_password($user_config, $password); // Set our new user's password - $user_config["scope"] = "user"; // Set our new user's system scope - $user_config["uid"] = $uid; // Set our new user's UID - $user_config["name"] = $username; // Set our new user's username - // Check for our optional parameters - if (!empty($descr)) { - $user_config["descr"] = $descr; // Set our new user's full name - } - if (!empty($expires)) { - $user_config["expires"] = $expires; // Set our new user's expiration date - } - if (!empty($authorizedkeys)) { - $user_config["authorizedkeys"] = $authorizedkeys; // Set our new user's authorized keys - } - if (!empty($ipsecpsk)) { - $user_config["ipsecpsk"] = $ipsecpsk; // Set our new user's IPsec pre-shared key - } - if ($disabled === true) { - $user_config["disabled"] = ""; // Set our new user's disabled value if not false - } - $user_config["priv"] = array(null); // Default our privs to empty array - // Append our new user's configuration to our user list, then set our configuration and write config - $config['system']['user'] = array_merge($config['system']['user'], array($user_config)); - $config["system"]["nextuid"] = strval(intval($uid) + 1); // Increase our next UID - local_user_set($user_config); - write_config(sprintf(gettext($change_note))); - // Check our updated config to ensure our change was successful - $userindex = index_users(); // Update our user index - $user_config =& getUserEntry($username); // Update our user config - // Check that our user is now in the user configuration - if (array_key_exists("uid", $user_config)) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added user"; - $api_resp["data"] = $user_config; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_add_privs() { - # VARIABLES - global $err_lib, $config, $priv_list, $userindex, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-usermanager-addprivs"); // Array of privileges allowing this action - $userindex = index_users(); // Index our users - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['username'])) { - $username = $client_params['username']; - $username = trim($username); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['priv'])) { - $new_priv = $client_params['priv']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5004); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our user already exists, if so exit on non-zero - $user_config =& getUserEntry($username); - $user_privs = get_user_privileges($user_config); - if (!array_key_exists("uid", $user_config)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5001); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Ensure our new priv is array, if it is a string create an array containing the string - if (is_string($new_priv)) { - $new_priv = array($new_priv); - } - if (is_array($new_priv)) { - // Loop through our new priv list and check that the privs are valid - foreach ($new_priv as $np) { - if (!array_key_exists($np, $priv_list)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5006); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (in_array($np, $user_privs)) { - $new_priv = \array_diff($new_priv, array($np)); - } - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5005); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "USERNAME:" . PHP_EOL; - echo var_dump($username) . PHP_EOL; - echo "USER EXISTING PRIVILEGES:" . PHP_EOL; - echo var_dump($user_privs) . PHP_EOL; - echo "USER NEW PRIVILEGES:" . PHP_EOL; - echo var_dump($new_priv) . PHP_EOL; - } - // Add debug data if requested - $_SESSION["Username"] = $client_id; // Save our session username for logging purposes - $change_note = " Modified privileges for user `" . $username . "' via API"; // Add a change note - $user_id = $userindex[$username]; // Save our user's array index ID - $user_config["priv"] = array_merge($user_config["priv"], $new_priv); // Set our user's new permissions - local_user_set($user_config); // Set user backend parameters - $config["system"]["user"][$user_id] = $user_config; // Add our new config - write_config(sprintf(gettext($change_note))); // Write our new config - // Check our updated config to ensure our change was successful - parse_config(true); // Update our entire config - $userindex = index_users(); // Update our user index - $user_config =& getUserEntry($username); // Update our user config - $user_privs = get_user_privileges($user_config); // Update our user privs - if ($user_config["priv"] === $user_privs) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added user privilege"; - $api_resp["data"] = $new_priv; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_add_groups() { - # VARIABLES - global $err_lib, $userindex, $groupindex, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-groupmanager"); // Allowed privs - $userindex = index_users(); // Index our users - $groupindex = index_groups(); // Index our groups - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['username'])) { - $username = $client_params['username']; - $username = trim($username); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['group'])) { - $new_groups = $client_params['group']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5007); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - - } - // Check if our user already exists, if so exit on non-zero - if (!array_key_exists($username, $userindex)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5001); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Ensure our new priv is array, if it is a string create an array containing the string - if (is_string($new_groups)) { - $new_groups = array($new_groups); - } - if (is_array($new_groups)) { - // Get our current user's groups - $user_config = getUserEntry($username); - $user_groups = local_user_get_groups($user_config, true); - // Loop through our new priv list and check that the privs are valid - foreach ($new_groups as $ng) { - if (!array_key_exists($ng, $groupindex)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5008); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (in_array($ng, $user_groups)) { - $new_groups = \array_diff($new_groups, array($ng)); - } - } - // Add our new groups to our existing groups - $new_groups = array_merge($user_groups, $new_groups); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5009); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "USERNAME:" . PHP_EOL; - echo var_dump($username) . PHP_EOL; - echo "USER GROUPS:" . PHP_EOL; - echo var_dump($user_groups) . PHP_EOL; - echo "USER NEW GROUPS:" . PHP_EOL; - echo var_dump($new_groups) . PHP_EOL; - } - // Add our group memberships - $_SESSION["Username"] = $client_id; // Save our session username for logging purposes - $change_note = " Added group membership for user `" . $username . "' via API"; // Add a change note - $user_id = $userindex[$username]; // Save our user's array index ID - $user_uid = $user_config["uid"]; // Save our user's UID - local_user_set_groups($user_config, $new_groups); // Set our user's groups - local_user_set($user_config); // Reset our user - write_config(sprintf(gettext($change_note))); // Write our config - // Update our current user's groups - $user_config = getUserEntry($username); - $user_groups = local_user_get_groups($user_config, true); - if (!array_diff($new_groups, $user_groups)) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added group memberships"; - $api_resp["data"] = $new_groups; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_authservers() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-authservers"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $as_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["system"]["authserver"])) { - $as_array = $config["system"]["authserver"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $as_array = api_extended_search($as_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $as_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_authservers_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-user-authserver"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $del_server = array(); // Init our deleted auth server config - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['name'])) { - $name = $client_params['name']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5010); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "AUTH SERVER NAME TO DELETE:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - } - // Loop through our servers and check that this server exists - foreach ($config["system"]["authserver"] as $asid => $asc) { - if ($name === $asc["name"]) { - $del_server = $config["system"]["authserver"][$asid]; // Save our config before deleting - unset($config["system"]["authserver"][$asid]); // Remove our config - // Check if this auth server is our default, if so revert to local database - if ($name === $config["system"]["webgui"]["authmode"]) { - unset($config["system"]["webgui"]["authmode"]); - } - } - } - // Add our auth server - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted authentication server via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted user authentication server"; - $api_resp["data"] = $del_server; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_authservers_radius() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-authservers"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $as_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["system"]["authserver"])) { - foreach ($config["system"]["authserver"] as $a) { - if ($a["type"] === "radius") { - $as_array[] = $a; - } - } - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $as_array = api_extended_search($as_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $as_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_authservers_radius_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-user-authserver"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $del_server = array(); // Init our deleted auth server config - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['name'])) { - $name = $client_params['name']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5010); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "AUTH SERVER NAME TO DELETE:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - } - // Loop through our servers and check that this server exists and is a RADIUS server - foreach ($config["system"]["authserver"] as $asid => $asc) { - if ($name === $asc["name"] and $asc["type"] === "radius") { - $del_server = $config["system"]["authserver"][$asid]; // Save our config before deleting - unset($config["system"]["authserver"][$asid]); // Remove our config - // Check if this auth server is our default, if so revert to local database - if ($name === $config["system"]["webgui"]["authmode"]) { - unset($config["system"]["webgui"]["authmode"]); - } - } - } - // Add our auth server - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted authentication server via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted RADIUS authentication server"; - $api_resp["data"] = $del_server; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_authservers_ldap() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-authservers"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $as_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["system"]["authserver"])) { - foreach ($config["system"]["authserver"] as $a) { - if ($a["type"] === "ldap") { - $as_array[] = $a; - } - } - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $as_array = api_extended_search($as_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $as_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_authservers_ldap_add() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params, $pf_ver_num; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-user-authserver"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $ldap_count = count($config["system"]["authserver"]); // Save our total number of auth servers - $ldap_ent = ["refid" => uniqid(), "type" => "ldap"]; // Init our new LDAP config - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['name'])) { - $ldap_ent["name"] = sanitize_str($client_params['name']); - if (is_authentication_server($ldap_ent["name"])) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5010); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - while (true) { - $ldap_ent["name"] = "LDAP_AUTHSERVER_" . strval($ldap_count); - // Check if auto name is already used - if (is_authentication_server($ldap_ent["name"])) { - $ldap_count++; - } else { - break; - } - } - } - if (isset($client_params['host'])) { - $ldap_ent["host"] = $client_params['host']; - // Check if our hostname is valid - if (!is_hostname($ldap_ent["host"]) and !is_ipaddrv4($ldap_ent["host"])) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5012); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5011); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (($client_params['ldap_port'])) { - $ldap_ent["ldap_port"] = strval($client_params['ldap_port']); - // Check if our hostname is valid - if (!is_port($ldap_ent["ldap_port"])) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5014); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5013); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ldap_urltype'])) { - // Assign default config values - $allowed_url_types = array( - "standard" => "TCP - Standard", - "starttls" => "TCP - STARTTLS", - "encrypted" => "SSL - Encrypted" - ); - // Accommodate config value changes for pfSense 2.5_ - if ($pf_ver_num >= 250) { - $allowed_url_types = array( - "standard" => "Standard TCP", - "starttls" => "STARTTLS Encrypted", - "encrypted" => "SSL/TLS Encrypted" - ); - } - // Check if our URL type is allowed - if (!array_key_exists($client_params["ldap_urltype"], $allowed_url_types)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5016); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - $ldap_ent["ldap_urltype"] = $allowed_url_types[$client_params['ldap_urltype']]; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5015); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ldap_protver'])) { - $ldap_ent["ldap_protver"] = intval($client_params['ldap_protver']); - // Check if our LDAP version is valid - $allowed_ldap_ver = array(2, 3); // Array of allowed LDAP protocol versions - if (!in_array($ldap_ent["ldap_protver"], $allowed_ldap_ver)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5017); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $ldap_ent["ldap_protver"] = 3; - } - if (isset($client_params['ldap_timeout'])) { - $ldap_ent["ldap_timeout"] = intval($client_params['ldap_timeout']); - } else { - $ldap_ent["ldap_timeout"] = 25; // Assign default if not specified - } - if (isset($client_params['ldap_scope'])) { - $ldap_ent["ldap_scope"] = $client_params['ldap_scope']; - // Check if our LDAP scope is valid - $allowed_ldap_scopes = array("one", "subtree"); // Array of allowed LDAP scopes - if (!in_array($ldap_ent["ldap_scope"], $allowed_ldap_scopes)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5019); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5018); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ldap_basedn'])) { - $ldap_ent["ldap_basedn"] = strval($client_params['ldap_basedn']); - } else { - $ldap_ent["ldap_basedn"] = ""; - } - if (isset($client_params['ldap_authcn'])) { - $ldap_ent["ldap_authcn"] = strval($client_params['ldap_authcn']); - } else { - $ldap_ent["ldap_authcn"] = ""; - } - if (isset($client_params['ldap_extended_query'])) { - $ldap_ent["ldap_extended_enabled"] = "yes"; - $ldap_ent["ldap_extended_query"] = strval($client_params['ldap_extended_query']); - } - if (!isset($client_params['ldap_binddn']) and !isset($client_params['ldap_bindpw'])) { - $bind_anon = true; - } - if (!$bind_anon) { - if (isset($client_params['ldap_binddn'])) { - $ldap_ent["ldap_binddn"] = strval($client_params['ldap_binddn']); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5020); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ldap_bindpw'])) { - $ldap_ent["ldap_bindpw"] = strval($client_params['ldap_bindpw']); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5021); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - if (isset($client_params['ldap_attr_user'])) { - $ldap_ent["ldap_attr_user"] = strval($client_params['ldap_attr_user']); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5022); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ldap_attr_group'])) { - $ldap_ent["ldap_attr_group"] = strval($client_params['ldap_attr_group']); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5023); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ldap_attr_member'])) { - $ldap_ent["ldap_attr_member"] = strval($client_params['ldap_attr_member']); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5024); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ldap_attr_groupobj'])) { - $ldap_ent["ldap_rfc2307"] = ""; // Enable RFC2307 mode - $ldap_ent["ldap_attr_groupobj"] = strval($client_params['ldap_attr_groupobj']); - } - if ($client_params['ldap_utf8'] === true) { - $ldap_ent["ldap_utf8"] = ""; // Enable UTF8 LDAP parameters - } - if ($client_params['ldap_nostrip_at'] === true) { - $ldap_ent["ldap_nostrip_at"] = ""; // Disable LDAP username alterations (@) - } - if ($client_params['active'] === true) { - $set_as_default = true; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "NAME:" . PHP_EOL; - echo var_dump($ldap_ent["name"]) . PHP_EOL; - echo "HOSTNAME OR IP:" . PHP_EOL; - echo var_dump($ldap_ent["host"]) . PHP_EOL; - echo "REMOTE LDAP PORT:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_port"]) . PHP_EOL; - echo "TRANSPORT METHOD:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_urltype"]) . PHP_EOL; - echo "LDAP PROTOCOL VERSION:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_protver"]) . PHP_EOL; - echo "LDAP TIMEOUT:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_timeout"]) . PHP_EOL; - echo "LDAP SEARCH SCOPE:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_scope"]) . PHP_EOL; - echo "BIND ANONYMOUSLY:" . PHP_EOL; - echo var_dump($bind_anon) . PHP_EOL; - echo "LDAP BASE DN:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_basedn"]) . PHP_EOL; - echo "LDAP AUTH CONTAINER:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_authcn"]) . PHP_EOL; - echo "LDAP EXTENDED QUERY:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_extended_query"]) . PHP_EOL; - echo "LDAP BIND DN:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_binddn"]) . PHP_EOL; - echo "LDAP BIND PASSWORD:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_bindpw"]) . PHP_EOL; - echo "LDAP USER ATTRITBUTE:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_attr_user"]) . PHP_EOL; - echo "LDAP GROUP ATTRITBUTE:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_attr_group"]) . PHP_EOL; - echo "LDAP MEMBER ATTRIBUTE:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_attr_member"]) . PHP_EOL; - echo "LDAP GROUP OBJECT:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_attr_groupobj"]) . PHP_EOL; - echo "ENABLE RFC2307:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_rfc2307"]) . PHP_EOL; - echo "ENABLE UTF-8 ENCODING:" . PHP_EOL; - echo var_dump($ldap_ent["ldap_utf8"]) . PHP_EOL; - } - // Add our auth server - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added authentication server via API"; // Add a change note - $config["system"]["authserver"][] = $ldap_ent; // Add our new configuration - write_config(sprintf(gettext($change_note))); // Apply our configuration change - // Check if clients wants to set this as default auth server - if ($set_as_default) { - $change_note = " Added authentication server via API"; // Add a change note - $config['system']['webgui']['authmode'] = $ldap_ent["name"]; // Add default auth server - write_config(sprintf(gettext($change_note))); // Apply our configuration change - } - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => ""); - $api_resp["message"] = "Successfully added LDAP authentication server"; - $api_resp["data"] = $ldap_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_users_authservers_ldap_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-user-authserver"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $del_server = array(); // Init our deleted auth server config - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['name'])) { - $name = $client_params['name']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 5011); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "AUTH SERVER NAME TO DELETE:" . PHP_EOL; - echo var_dump($name) . PHP_EOL; - } - // Loop through our servers and check that this server exists and is an LDAP server - foreach ($config["system"]["authserver"] as $asid => $asc) { - if ($name === $asc["name"] and $asc["type"] === "ldap") { - $del_server = $config["system"]["authserver"][$asid]; // Save our config before deleting - unset($config["system"]["authserver"][$asid]); // Remove our config - // Check if this auth server is our default, if so revert to local database - if ($name === $config["system"]["webgui"]["authmode"]) { - unset($config["system"]["webgui"]["authmode"]); - } - } - } - // Add our auth server - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted authentication server via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted LDAP authentication server"; - $api_resp["data"] = $del_server; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_routing_gateways() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-gateways"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $gw_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["gateways"]["gateway_item"])) { - $gw_array = $config["gateways"]["gateway_item"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $gw_array = api_extended_search($gw_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $gw_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_ntpd_start() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_ntpd_stop() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_ntpd_restart() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (READ) - if ($http_method === 'GET') { - if (isset($client_params['name'])) { - $name = $client_params['name']; - } - $service_list = get_services(); // Stop our service - // Loop through our service list and add our service status - foreach ($service_list as $key => $srvc) { - $s_status = get_service_status($srvc); - // Check if service is started - if ($s_status === true) { - $service_list[$key]["status"] = "running"; - } elseif ($s_status === false) { - $service_list[$key]["status"] = "stopped"; - } else { - $service_list[$key]["status"] = "unknown"; - } - // Check if user requested only one service, if so remove unmatched services - if (isset($name) and $name !== $srvc["name"]) { - unset($service_list[$key]); - } - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $service_list); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_sshd() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-advanced-admin"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $ssh_array = array(); // Init our return array - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["system"]["ssh"])) { - $ssh_array = $config["system"]["ssh"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $ssh_array = api_extended_search($ssh_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $ssh_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_sshd_modify() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-system-advanced-admin"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $err_found = false; // Track errors - $allowed_auth_types = array("disabled", "enabled", "both"); // Array of allowed auth types - - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - $api_resp = array("status" => "bad request", "code" => 400); - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['enable'])) { - $enable = $client_params['enable']; - if ($enable === true) { - $config["system"]["ssh"]["enable"] = "enabled"; - } elseif ($enable === false) { - unset($config["system"]["ssh"]["enable"]); - } else { - $api_resp["return"] = 2000; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - if (isset($client_params["sshdkeyonly"])) { - $sshdkeyonly = $client_params["sshdkeyonly"]; - // Check if our auth type is valid - if (in_array($sshdkeyonly, $allowed_auth_types)) { - if ($sshdkeyonly === "disabled") { - unset($config["system"]["ssh"]["sshdkeyonly"]); - } else { - $config["system"]["ssh"]["sshdkeyonly"] = $sshdkeyonly; - } - } else { - $api_resp["return"] = 2001; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - if (isset($client_params['sshdagentforwarding'])) { - $sshdagentforwarding = $client_params['sshdagentforwarding']; - if ($sshdagentforwarding === true) { - $config["system"]["ssh"]["sshdagentforwarding"] = "enabled"; - } elseif ($sshdagentforwarding === false) { - unset($config["system"]["ssh"]["sshdagentforwarding"]); - } else { - $api_resp["return"] = 2002; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - if (isset($client_params['port'])) { - $port = strval($client_params['port']); - // Convert string to array - if (is_port($port)) { - $config["system"]["ssh"]["port"] = $port; - } else { - $api_resp["return"] = 2003; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "ENABLE SSHD:" . PHP_EOL; - echo var_dump($enable) . PHP_EOL; - echo "SSHD KEY ONLY:" . PHP_EOL; - echo var_dump($sshdkeyonly) . PHP_EOL; - echo "SSHD AGENT FORWARDING:" . PHP_EOL; - echo var_dump($sshdagentforwarding) . PHP_EOL; - echo "SSHD PORT:" . PHP_EOL; - echo var_dump($port) . PHP_EOL; - } - // Modify our SSHD config - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Modified sshd configuration via API"; // Add a change note - write_config(sprintf(gettext($change_note))); // Apply our configuration change - // Check that something was changed before altering service - if (isset($enable) or isset($port) or isset($sshdagentforwarding) or isset($sshdkeyonly)) { - killbyname("sshd"); // Kill SSHD - log_error(gettext("secure shell configuration has changed. Stopping sshd.")); - if ($config['system']['ssh']['enable']) { - log_error(gettext("secure shell configuration has changed. Restarting sshd.")); - send_event("service restart sshd"); - } - } - // Loop through each alias and see if our alias was added successfully - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully modified sshd configuration"; - $api_resp["data"] = $config["system"]["ssh"]; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_sshd_start() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_sshd_stop() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_sshd_restart() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_syslogd_start() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_syslogd_stop() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_syslogd_restart() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_start() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - // Check if user specified which services to set - if (isset($client_params['service'])) { - $service = is_array($client_params['service']) ? $client_params['service'] : [$client_params['service']]; - } - $service_list = get_services(); - $services_set = []; - // Loop through our service list and add our service status - foreach ($service_list as $key => $srvc) { - // Check if this service should be set - if (!isset($service) or in_array($srvc["name"], $service)) { - $set_service = service_control_start($srvc["name"], []); // Start our service - $services_set[] = $srvc["name"]; - } - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "Successfully started all services"); - $api_resp["data"] = $services_set; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_stop() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - // Check if user specified which services to set - if (isset($client_params['service'])) { - $service = is_array($client_params['service']) ? $client_params['service'] : [$client_params['service']]; - } - $service_list = get_services(); - $services_set = []; - // Loop through our service list and add our service status - foreach ($service_list as $key => $srvc) { - // Check if this service should be set - if (!isset($service) or in_array($srvc["name"], $service)) { - $set_service = service_control_stop($srvc["name"], []); // Stop our service - $services_set[] = $srvc["name"]; - } - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "Successfully stopped all services"); - $api_resp["data"] = $services_set; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_restart() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - // Check if user specified which services to set - if (isset($client_params['service'])) { - $service = is_array($client_params['service']) ? $client_params['service'] : [$client_params['service']]; - } - $service_list = get_services(); - $services_set = []; - // Loop through our service list and add our service status - foreach ($service_list as $key => $srvc) { - // Check if this service should be set - if (!isset($service) or in_array($srvc["name"], $service)) { - $set_service = service_control_restart($srvc["name"], []); // Restart our service - $services_set[] = $srvc["name"]; - } - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "Successfully restarted all services"); - $api_resp["data"] = $services_set; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_dhcpd_start() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_dhcpd_stop() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_dhcpd_restart() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_dpinger_start() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_dpinger_stop() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_dpinger_restart() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_unbound() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-services-dnsresolver"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - // Check that we have a configuration - if (!empty($config["unbound"])) { - $unbound_array = $config["unbound"]; - } - if (isset($client_params['search'])) { - $search = $client_params['search']; - $unbound_array = api_extended_search($unbound_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $unbound_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_unbound_delete_hosts() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-services-dnsresolver-edithost"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $del_mode = ""; - // FUNCTIONS------------------------------------------------------------------------------------------------------------ - function host_cmp($a, $b) { - return strcasecmp($a['host'], $b['host']); - } - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (DELETE) - if ($http_method === 'POST') { - if (isset($client_params['host'])) { - $hostname = $client_params['host']; - $hostname = trim($hostname); - $h_mode = true; - } - if (isset($client_params['domain'])) { - $domain = $client_params['domain']; - $domain = trim($domain); - $d_mode = true; - } - if (isset($client_params['ip'])) { - $ipaddr = $client_params['ip']; - $ipaddr = trim($ipaddr); - $i_mode = true; - } - if ($client_params['aliases'] === true) { - $a_mode = true; - } - if ($client_params['apply'] === true) { - $apply = $client_params['apply']; - } - // Determine criteria for deletion - if ($h_mode and !$d_mode and !$i_mode) { - $del_mode = "h"; - } elseif ($h_mode and $d_mode and !$i_mode) { - $del_mode = "hd"; - } elseif ($h_mode and !$d_mode and $i_mode) { - $del_mode = "hi"; - } elseif ($h_mode and $d_mode and $i_mode) { - $del_mode = "hdi"; - } elseif (!$h_mode and $d_mode and !$i_mode) { - $del_mode = "d"; - } elseif (!$h_mode and $d_mode and $i_mode) { - $del_mode = "di"; - } elseif (!$h_mode and !$d_mode and $i_mode) { - $del_mode = "i"; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 45); - $api_resp["message"] = "host override deletion criteria not met"; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "HOSTNAME:" . PHP_EOL; - echo var_dump($hostname) . PHP_EOL; - echo "DOMAIN:" . PHP_EOL; - echo var_dump($domain) . PHP_EOL; - echo "IP ADDRESS:" . PHP_EOL; - echo var_dump($ipaddr) . PHP_EOL; - echo "ALIASES:" . PHP_EOL; - echo var_dump($a_mode) . PHP_EOL; - echo "MODE:" . PHP_EOL; - echo var_dump($del_mode) . PHP_EOL; - echo "APPLY:" . PHP_EOL; - echo var_dump($apply) . PHP_EOL; - } - // Check that our configuration is a list and loop through each item, otherwise return ok resp - if (array_key_exists("hosts", $config["unbound"]) and is_array($config["unbound"]["hosts"])) { - $del_list = array("hosts" => array(), "aliases" => array()); // List of deleted items - $hosts_conf = &$config["unbound"]["hosts"]; // Current Unbound host overrides - $h_count = 0; // Define counter for our hosts loop - foreach ($hosts_conf as $he) { - // Check aliases for match if alias mode - if ($a_mode and is_array($he["aliases"])) { - $a_count = 0; // Define counter for our aliases loop - // Loop through aliases to check for matches - foreach ($he["aliases"]["item"] as $ae) { - if ($del_mode === "h") { - if ($hostname === $ae["host"]) { - unset($hosts_conf[$h_count]["aliases"]["item"][$a_count]); - $del_list["aliases"][] = $ae["host"].".".$ae["domain"]; - } - } elseif ($del_mode === "d") { - if ($domain === $ae["domain"]) { - unset($hosts_conf[$h_count]["aliases"]["item"][$a_count]); - $del_list["aliases"][] = $ae["host"].".".$ae["domain"]; - } - } elseif ($del_mode === "hd") { - if ($hostname === $ae["host"] and $domain === $ae["domain"]) { - unset($hosts_conf[$h_count]["aliases"]["item"][$a_count]); - $del_list["aliases"][] = $ae["host"].".".$ae["domain"]; - } - } - // If all aliases were removed, restore aliases key to empty string - if (empty($hosts_conf[$h_count]["aliases"]["item"])) { - $hosts_conf[$h_count]["aliases"] = ""; - } - // Increase our alias counter - $a_count++; - } - } - // Check parent host entries - if ($del_mode === "h") { - if ($hostname === $he["host"]) { - unset($hosts_conf[$h_count]); - $del_list["hosts"][] = $he["host"].".".$he["domain"]; - } - } elseif ($del_mode === "d") { - if ($domain === $he["domain"]) { - unset($hosts_conf[$h_count]); - $del_list["hosts"][] = $he["host"].".".$he["domain"]; - } - } elseif ($del_mode === "i") { - if ($ipaddr === $he["ip"]) { - unset($hosts_conf[$h_count]); - $del_list["hosts"][] = $he["host"].".".$he["domain"]; - } - } elseif ($del_mode === "hd") { - if ($hostname === $he["host"] and $domain === $he["domain"]) { - unset($hosts_conf[$h_count]); - $del_list["hosts"][] = $he["host"].".".$he["domain"]; - } - } elseif ($del_mode === "hi") { - if ($hostname === $he["host"] and $ipaddr === $he["ip"]) { - unset($hosts_conf[$h_count]); - $del_list["hosts"][] = $he["host"].".".$he["domain"]; - } - } elseif ($del_mode === "di") { - if ($domain === $he["domain"] and $ipaddr === $he["ip"]) { - unset($hosts_conf[$h_count]); - $del_list["hosts"][] = $he["host"].".".$he["domain"]; - } - } elseif ($del_mode === "hdi") { - if ($hostname === $he["host"] and $domain === $he["domain"] and $ipaddr === $he["ip"]) { - unset($hosts_conf[$h_count]); - $del_list["hosts"][] = $he["host"].".".$he["domain"]; - } - } - // Increase our host counter - $h_count++; - } - // Sort and write our new configuration - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted DNS Resolver host override via API"; // Add a change note - usort($hosts_conf, "strcmp"); - $config["unbound"]["hosts"] = $hosts_conf; - write_config(sprintf(gettext($change_note))); - mark_subsystem_dirty("unbound"); - # If user requests immediately application - if ($apply === true) { - $applied = unbound_reload_config(); - } - // Return success if our function was successful - if ($applied === true or $apply !== true) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "host override deleted"; - $api_resp["data"] = $del_list; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 4, "message" => "process fail"); - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2013); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_unbound_modify_hosts() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-services-dnsresolver-edithost"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - // FUNCTIONS------------------------------------------------------------------------------------------------------------ - function host_cmp($a, $b) { - return strcasecmp($a['host'], $b['host']); - } - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - if (isset($client_params['host'])) { - $hostname = $client_params['host']; - $hostname = trim($hostname); - $h_mode = true; - } - if (isset($client_params['new_host'])) { - $new_hostname = $client_params['new_host']; - $new_hostname = trim($new_hostname); - } - if (isset($client_params['domain'])) { - $domain = $client_params['domain']; - $domain = trim($domain); - } elseif ($h_mode) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2005); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['new_domain'])) { - $new_domain = $client_params['new_domain']; - $new_domain = trim($new_domain); - } - if (isset($client_params['ip']) and !$h_mode) { - $ipaddr = $client_params['ip']; - $ipaddr = trim($ipaddr); - $i_mode = true; - } - if (isset($client_params['new_ip'])) { - $new_ipaddr = $client_params['new_ip']; - $new_ipaddr = trim($new_ipaddr); - } elseif ($i_mode) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2012); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } - if (isset($client_params['aliases'])) { - $aliases = $client_params['aliases']; - } - if ($client_params['apply'] === true) { - $apply = $client_params['apply']; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "HOSTNAME:" . PHP_EOL; - echo var_dump($hostname) . PHP_EOL; - echo "DOMAIN:" . PHP_EOL; - echo var_dump($domain) . PHP_EOL; - echo "IP ADDRESS:" . PHP_EOL; - echo var_dump($ipaddr) . PHP_EOL; - echo "NEW HOSTNAME:" . PHP_EOL; - echo var_dump($new_hostname) . PHP_EOL; - echo "NEW DOMAIN:" . PHP_EOL; - echo var_dump($new_domain) . PHP_EOL; - echo "NEW IP ADDRESS:" . PHP_EOL; - echo var_dump($new_ipaddr) . PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "ALIASES:" . PHP_EOL; - echo var_dump($aliases) . PHP_EOL; - echo "APPLY:" . PHP_EOL; - echo var_dump($aliases) . PHP_EOL; - } - // Validate our input against our exist configuration - if (unbound_host_override_exists($hostname, $domain) or $i_mode) { - $hosts_conf = &$config["unbound"]["hosts"]; // Current Unbound host overrides - $h_count = 0; // Assign a loop counter - $update_list = array(); // Assign array to track which values were updated - // Check modification mode - if ($i_mode) { - if (is_ipaddrv4($new_ipaddr) or is_ipaddrv6($new_ipaddr)) { - foreach ($hosts_conf as $he) { - // If our IP matches, update our IP - if ($ipaddr === $he["ip"]) { - $hosts_conf[$h_count]["ip"] = $new_ipaddr; - $update_list[] = $hosts_conf[$h_count]; - } - // Increase our counter - $h_count++; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2011); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } elseif ($h_mode) { - foreach ($hosts_conf as $he) { - $he_updated = false; - // Check if both our hostname and domain names were changed - if (isset($new_hostname) and isset($new_domain)) { - if ($hostname === $he["host"] and $domain === $he["domain"]) { - if (!unbound_host_override_exists($new_hostname, $new_domain)) { - $hosts_conf[$h_count]["host"] = $new_hostname; - $hosts_conf[$h_count]["domain"] = $new_domain; - $he_updated = true; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2010); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } elseif (isset($new_hostname)) { - if ($hostname === $he["host"] and $domain === $he["domain"]) { - if (!unbound_host_override_exists($new_hostname, $he["domain"])) { - $hosts_conf[$h_count]["host"] = $new_hostname; - $he_updated = true; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2010); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } elseif (isset($new_domain)) { - if ($hostname === $he["host"] and $domain === $he["domain"]) { - if (!unbound_host_override_exists($he["host"], $new_domain)) { - $hosts_conf[$h_count]["domain"] = $new_domain; - $he_updated = true; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2010); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - if (isset($new_ipaddr)) { - if (is_ipaddrv4($new_ipaddr) or is_ipaddrv6($new_ipaddr)) { - if ($hostname === $he["host"] and $domain === $he["domain"]) { - $hosts_conf[$h_count]["ip"] = $new_ipaddr; - $he_updated = true; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2011); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - if (isset($descr)) { - if ($hostname === $he["host"] and $domain === $he["domain"]) { - $hosts_conf[$h_count]["descr"] = $descr; - $he_updated = true; - } - } - if (isset($aliases)) { - // Check if we have more than one - if (count($update_list) <= 1) { - $aliases_fin = unbound_parse_aliases($aliases); // Parse our aliases - if ($aliases_fin !== "") { - $hosts_conf[$h_count]["aliases"] = $aliases_fin; - $he_updated = true; - } - } - } - // Check if our entry was updated, if so add it to our update list - if ($he_updated) { - $update_list[] = $hosts_conf[$h_count]; - } - // Increase our counter - $h_count++; - } - } - // Sort and write our new configuration - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Modified DNS Resolver host override via API"; // Add a change note - usort($hosts_conf, "strcmp"); - $config["unbound"]["hosts"] = $hosts_conf; - write_config(sprintf(gettext($change_note))); - mark_subsystem_dirty("unbound"); - # If user requests immediately application - if ($apply === true) { - $applied = unbound_reload_config(); - } - // Return success if our function was successful - if ($applied === true or $apply !== true) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully updated unbound host override"; - $api_resp["data"] = $update_list; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2013); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_unbound_start() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_unbound_stop() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_unbound_restart() { - # VARIABLES - global $err_lib, $api_resp; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-status-services"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $uri_parse = explode("/", $_SERVER["REQUEST_URI"]); // Save our URI - $service = $uri_parse[4]; // Save our service name - $action = $uri_parse[5]; // Save our action (start, stop, restart) - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (UPDATE) - if ($http_method === 'POST') { - # Check our aciton - if ($action === "start") { - $set_service = service_control_start($service, []); // Start our service - $act_str = "started"; - } elseif ($action === "stop") { - $set_service = service_control_stop($service, []); // Stop our service - $act_str = "stopped"; - } elseif ($action === "restart") { - $set_service = service_control_restart($service, []); // Restart our service - $act_str = "restarted"; - } else { - $act_failed = true; - } - // Check if our action succeeded or failed - if (!$act_failed) { - // Print our success response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully ".$act_str." service ".$service; - return $api_resp; - } else { - // Print our fail response - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_unbound_add_hosts() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-services-dnsresolver-edithost"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - // FUNCTIONS------------------------------------------------------------------------------------------------------------ - function host_cmp($a, $b) { - return strcasecmp($a['host'], $b['host']); - } - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['host'])) { - $hostname = $client_params['host']; - $hostname = trim($hostname); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2004); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['domain'])) { - $domain = $client_params['domain']; - $domain = trim($domain); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2005); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['ip'])) { - $ipaddr = $client_params['ip']; - $ipaddr = trim($ipaddr); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2006); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } - if (isset($client_params['aliases'])) { - $aliases = $client_params['aliases']; - } - if ($client_params['apply'] === true) { - $apply = $client_params['apply']; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "HOSTNAME:" . PHP_EOL; - echo var_dump($hostname) . PHP_EOL; - echo "DOMAIN:" . PHP_EOL; - echo var_dump($domain) . PHP_EOL; - echo "IP ADDRESS:" . PHP_EOL; - echo var_dump($ipaddr) . PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "ALIASES:" . PHP_EOL; - echo var_dump($aliases) . PHP_EOL; - echo "APPLY:" . PHP_EOL; - echo var_dump($apply) . PHP_EOL; - } - // Validate our input against our exist configuration - if (!unbound_host_override_exists($hostname, $domain)) { - if (is_ipaddrv4($ipaddr) or is_ipaddrv6($ipaddr)) { - $aliases_fin = unbound_parse_aliases($aliases); // Parse our aliases - // Add our host override - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added host override to DNS Resolver via API"; // Add a change note - $host_ent = array(); - $host_ent["host"] = $hostname; - $host_ent["domain"] = $domain; - $host_ent["ip"] = $ipaddr; - $host_ent["descr"] = $descr; - $host_ent["aliases"] = $aliases_fin; - $config["unbound"]["hosts"][] = $host_ent; - usort($config["unbound"]["hosts"], "host_cmp"); - write_config(sprintf(gettext($change_note))); - mark_subsystem_dirty("unbound"); - # If user requests immediately application - if ($apply === true) { - $applied = unbound_reload_config(); - } - // Return success if our function was successful - if ($applied === true or $apply !== true) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added unbound host override"; - $api_resp["data"] = $host_ent; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2011); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2010); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_services_unbound_apply() { - # VARIABLES; - global $err_lib; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-services-dnsresolver-edithost"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - // Check if application was successful - if (unbound_reload_config() === true) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully applied unbound configuration"; - $api_resp["data"] = ""; - return $api_resp; - } else { - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } -} - - -function api_interfaces_vlans() { - # VARIABLES - global $err_lib, $g, $config, $argv, $userindex, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-interfaces-vlan"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $vlan_array = array(); - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - if (isset($client_params['search'])) { - $search = $client_params['search']; - $vlan_array = api_extended_search($vlan_array, $search); - } - if (!empty($config["vlans"]["vlan"])) { - $vlan_array = $config["vlans"]["vlan"]; - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $vlan_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_interfaces_vlans_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-interfaces-vlan-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $if_list = get_interface_list(); // Get our interfaces list - $lagg_list = get_lagg_interface_list(); // Get our lagg interfaces list - $avail_ifs = $if_list + $lagg_list; // Combine the two lists - $curr_vlans = $config["vlans"]["vlan"]; // Save our current VLANs - $del_ent = []; // Init our return data, this will be populated with data of deleted vlan if exists - // Remove LAGG interface members as they cannot be assigned VLANs - foreach ($lagg_list as $lagg_if => $lagg) { - $lagg_members = explode(',', $lagg['members']); - foreach ($lagg_members as $lagm) { - if (isset($avail_ifs[$lagm])) { - unset($avail_ifs[$lagm]); - } - } - } - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (Delete) - if ($http_method === 'POST') { - if (isset($client_params['vlanif'])) { - $vlan_if = $client_params['vlanif']; - } - elseif (isset($client_params['id'])) { - $id = $client_params['id']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 3048); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Ensure we have a vlanif and id regardless of which input selector was provided - if (isset($vlan_if)) { - foreach ($curr_vlans as $ind => $cve) { - if ($vlan_if === $cve["vlanif"]) { - $id = $ind; - break; - } - } - } else { - $vlan_if = $curr_vlans[$id]["vlanif"]; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "VLAN INTERFACE TO DELETE:" . PHP_EOL; - echo var_dump($vlan_if) . PHP_EOL; - echo "VLAN ID TO DELETE:" . PHP_EOL; - echo var_dump($id) . PHP_EOL; - } - // Check that our interface is not in use currently - if (convert_real_interface_to_friendly_interface_name($vlan_if)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 3049); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check that we have both our ID and VLAN interface - if (isset($id) and isset($vlan_if)) { - // Add our VLAN - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Deleted interface VLAN via API"; // Add a change note - $del_ent = $config["vlans"]["vlan"][$id]; // Save our deleted VLAN - pfSense_interface_destroy($config["vlans"]["vlan"][$id]["vlanif"]); // delete our VLAN on the backend - unset($config["vlans"]["vlan"][$id]); // Remove our VLAN configuration - write_config(sprintf(gettext($change_note))); // Apply our configuration change - } - // Return our success data - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully deleted interface VLAN"; - $api_resp["data"] = $del_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_interfaces_vlans_modify() { - # VARIABLES - global $err_lib, $g, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-interfaces-vlan-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $if_list = get_interface_list(); // Get our interfaces list - $lagg_list = get_lagg_interface_list(); // Get our lagg interfaces list - $avail_ifs = $if_list + $lagg_list; // Combine the two lists - $curr_vlans = $config["vlans"]["vlan"]; // Save our current VLANs - // Remove LAGG interface members as they cannot be assigned VLANs - foreach ($lagg_list as $lagg_if => $lagg) { - $lagg_members = explode(',', $lagg['members']); - foreach ($lagg_members as $lagm) { - if (isset($avail_ifs[$lagm])) { - unset($avail_ifs[$lagm]); - } - } - } - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['vlanif'])) { - $vlan_if = $client_params['vlanif']; - } - elseif (isset($client_params['id'])) { - $id = $client_params['id']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 3048); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['if'])) { - $interface = $client_params['if']; - } - if (isset($client_params['tag'])) { - $tag = $client_params['tag']; - $tag = intval(trim($tag)); - $str_tag = strval($tag); - } - if (isset($client_params['pcp'])) { - $pcp = $client_params['pcp']; - $pcp = intval(trim($pcp)); - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "PARENT INTERFACE:" . PHP_EOL; - echo var_dump($interface) . PHP_EOL; - echo "VLAN TAG:" . PHP_EOL; - echo var_dump($tag) . PHP_EOL; - echo "VLAN PRIORITY:".PHP_EOL; - echo var_dump($pcp).PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - } - // Ensure we have a vlanif and id regardless of which input selector was provided - if (isset($vlan_if)) { - foreach ($curr_vlans as $ind => $cve) { - if ($vlan_if === $cve["vlanif"]) { - $id = $ind; - break; - } - } - } else { - $vlan_if = $curr_vlans[$id]["vlanif"]; - } - // Input validation - $api_resp = array("status" => "bad request", "code" => 400); - // Check if our parent interface exists - if (!array_key_exists($id, $curr_vlans)) { - $api_resp["return"] = 3050; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (isset($interface) and !does_interface_exist($interface)) { - $api_resp["return"] = 3051; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (isset($tag) and (1 > $tag or $tag > 4096)) { - $api_resp["return"] = 3052; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (isset($pcp) and (0 > $pcp or $pcp > 7)) { - $api_resp["return"] = 3053; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our VLAN is already in use - if (is_array($curr_vlans)) { - if (isset($interface) and isset($tag)) { - foreach ($curr_vlans as $vle) { - if ($interface === $vle["if"] and $str_tag === $vle["tag"]) { - $api_resp["return"] = 3054; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - } - // Modify our VLAN - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Modified VLAN interface via API"; // Add a change note - $vlan_ent = $curr_vlans[$id]; // Pull our target VLAN's current configuration - $assigned_if = get_pfsense_if_id($vlan_if); // Check if an interface is using this VLAN if - $prev_vlanif = $vlan_if; // Save our previous VLAN interface ID - // Save our vlan interface if defined - if (isset($interface)) { - $vlan_ent["if"] = $interface; - } - // Save our tag if defined - if (isset($tag)) { - $vlan_ent["tag"] = $tag; - } - // Save our priority if defined - if (isset($pcp)) { - $vlan_ent["pcp"] = $pcp; - } - // Save our description if defined - if (isset($descr)) { - $vlan_ent["descr"] = $descr; - } - $vlan_ent["vlanif"] = $vlan_ent["if"].".".$vlan_ent["tag"]; // Format our physical interface ID - $config["vlans"] = !is_array($config["vlans"]) ? [] : $config["vlans"]; // Init empty VLAN array if needed - $config["vlans"]["vlan"][$id] = $vlan_ent; // Write our configuration change - pfSense_interface_destroy($prev_vlanif); // Delete our previous VLAN on the backend - interface_vlan_configure($vlan_ent); // Configure our modified VLAN on the backend - // Check if we need to reassign an interface - if (!empty($assigned_if)) { - $config['interfaces'][$assigned_if]['if'] = $vlan_ent["vlanif"]; // Write interface config - write_config(sprintf(gettext($change_note))); // Apply our configuration change - interface_configure($assigned_if); // Configure our assigned interface - } else { - write_config(sprintf(gettext($change_note))); // Apply our configuration change - } - // Return success - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully modified interface VLAN"; - $api_resp["data"] = $vlan_ent; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_interfaces_vlans_add() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-interfaces-vlan-edit"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $if_list = get_interface_list(); // Get our interfaces list - $lagg_list = get_lagg_interface_list(); // Get our lagg interfaces list - $avail_ifs = $if_list + $lagg_list; // Combine the two lists - $curr_vlans = $config["vlans"]["vlan"]; // Save our current VLANs - // Remove LAGG interface members as they cannot be assigned VLANs - foreach ($lagg_list as $lagg_if => $lagg) { - $lagg_members = explode(',', $lagg['members']); - foreach ($lagg_members as $lagm) { - if (isset($avail_ifs[$lagm])) { - unset($avail_ifs[$lagm]); - } - } - } - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['if'])) { - $interface = $client_params['if']; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 3055); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['tag'])) { - $tag = $client_params['tag']; - $tag = intval(trim($tag)); - $str_tag = strval($tag); - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 3048); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - if (isset($client_params['pcp'])) { - $pcp = $client_params['pcp']; - $pcp = intval(trim($pcp)); - - } - if (isset($client_params['descr'])) { - $descr = $client_params['descr']; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "PARENT INTERFACE:" . PHP_EOL; - echo var_dump($interface) . PHP_EOL; - echo "VLAN TAG:" . PHP_EOL; - echo var_dump($tag) . PHP_EOL; - echo "VLAN PRIORITY:".PHP_EOL; - echo var_dump($pcp).PHP_EOL; - echo "DESCRIPTION:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - } - // Input validation - $api_resp = array("status" => "bad request", "code" => 400); - // Check if our parent interface exists - if (!does_interface_exist($interface)) { - $api_resp["return"] = 3051; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (1 > $tag or $tag > 4096) { - $api_resp["return"] = 3052; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (0 > $pcp or $pcp > 7) { - $api_resp["return"] = 3053; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our VLAN is already in use - if (is_array($curr_vlans)) { - foreach ($curr_vlans as $vle) { - if ($interface === $vle["if"] and $str_tag === $vle["tag"]) { - $api_resp["return"] = 3054; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - // Add our VLAN - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added interface VLAN via API"; // Add a change note - $vlan_ent = array(); // Init our array - $vlan_ent["if"] = $interface; // Save our alias name - $vlan_ent["tag"] = $tag; // Save our type - $vlan_ent["pcp"] = isset($pcp) ? $pcp : ""; // Save our priority if provided - $vlan_ent["descr"] = isset($descr) ? $descr : ""; // Save our priority if provided - $vlan_ent["vlanif"] = $interface.".".$tag; // Format our physical interface ID - $config["vlans"] = !is_array($config["vlans"]) ? [] : $config["vlans"]; // Init empty VLAN array if needed - $config["vlans"]["vlan"][] = $vlan_ent; // Write our configuration change - interface_vlan_configure($vlan_ent); // Configure our VLAN on the backend - write_config(sprintf(gettext($change_note))); // Apply our configuration change - // Loop through each alias and see if our VLAN was added successfully - foreach ($config["vlans"]["vlan"] as $se) { - if ($interface === $se["if"] and $str_tag === $se["tag"]) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0); - $api_resp["message"] = "Successfully added interface VLAN"; - $api_resp["data"] = $se; - return $api_resp; - } - } - // Return error response if our loop did not find a match - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_interfaces_delete() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-interfaces-assignnetworkports"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - if (isset($client_params['if'])) { - $interface = $client_params['if']; - $interface = get_pfsense_if_id($interface); - if (empty($interface)) { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 3000); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 3002); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "INTERFACE TO DELETE:" . PHP_EOL; - echo var_dump($interface) . PHP_EOL; - } - // Add our VLAN - $curr_config = $config["interfaces"][$interface]; // Save our interface to delete - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $del_stat = destroy_interface($interface); // Destroy our interface - if ($del_stat["status"] === true) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "interface deleted"); - $api_resp["data"] = $curr_config; - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => $del_stat["msg"]); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_interfaces() { - # VARIABLES - global $err_lib, $config, $api_resp, $client_params; - $read_only_action = true; // Set whether this action requires read only access - $req_privs = array("page-all", "page-interfaces-assignnetworkports"); // Array of privs allowed - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is GET (READ) - if ($http_method === 'GET') { - $interface_array = $config["interfaces"]; - if (isset($client_params['search'])) { - $search = $client_params['search']; - $interface_array = api_extended_search($interface_array, $search); - } - // Print our JSON response - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "", "data" => $interface_array); - return $api_resp; - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - -function api_interfaces_add() { - # VARIABLES - global $err_lib, $g, $config, $api_resp, $client_id, $client_params; - $read_only_action = false; // Set whether this action requires read only access - $req_privs = array("page-all", "page-interfaces"); // Array of privileges allowing this action - $http_method = $_SERVER['REQUEST_METHOD']; // Save our HTTP method - $if_list = get_all_avail_interfaces(); // Save all our available interfaces - $allowed_ip4_types = array("staticv4", "dhcp"); // List of allowed IPv4 if types - $allowed_ip6_types = array("staticv6", "dhcp6", "slaac", "6rd", "6to4", "track6"); // List of allowed IPv6 if types - $next_if = get_next_pfsense_if_id(); - $if_ent = array($next_if => []); - # RUN TIME - // Check that client is authenticated and authorized - if (api_authorized($req_privs, $read_only_action)) { - // Check that our HTTP method is POST (CREATE) - if ($http_method === 'POST') { - $api_resp = array("status" => "bad request", "code" => 400); - // Get our requested physical interface - if (isset($client_params["if"])) { - $interface = trim($client_params["if"]); - // Check that our interface exists and is not in use - if (!array_key_exists($interface, $if_list)) { - $api_resp["return"] = 3000; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (isset($if_list[$interface]["in_use"])) { - $api_resp["return"] = 3001; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - $if_ent[$next_if]["if"] = $interface; - } else { - $api_resp["return"] = 3002; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check for our enable value - if (isset($client_params["enable"])) { - $enable = true; - $if_ent[$next_if]["enable"] = ""; - } - // Check for our MAC address value - if (isset($client_params["spoofmac"])) { - $mac_addr = $client_params["spoofmac"]; - // Check if mac addr is valid - if (is_macaddr($mac_addr)) { - $if_ent[$next_if]["spoofmac"] = $mac_addr; - } else { - $api_resp["return"] = 3003; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Check for our MTU value - if (isset($client_params["mtu"])) { - $mtu = intval($client_params["mtu"]); - // Check if MTU is within range - if (1280 > $mtu or $mtu > 8192) { - $api_resp["return"] = 3004; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif ($if_list[$interface]["is_vlan"]) { - // Check if interface is VLAN and that it's MTU is lower than it's parent interface - $parent_if = $if_list[$interface]["if"]; - if ($mtu > $parent_if["mtu"]) { - $api_resp["return"] = 3006; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $if_ent[$next_if]["mtu"] = $mtu; - } - } - // Check for our MSS value - if (isset($client_params["mss"])) { - $mss = intval($client_params["mss"]); - // Check if MSS is within range - if (576 > $mss or $mss > 65535) { - $api_resp["return"] = 3005; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $if_ent[$next_if]["mss"] = $mss; - } - } - // Check for our SPEED/DUPLEX value - if (isset($client_params["media"])) { - $media = $client_params["media"]; - $avail_media = get_if_media_options($interface, true); - // Loop each of our media options and see if our input matches - foreach ($avail_media as $mopt) { - if ($media === $mopt) { - $media_found = true; - $mopt_list = explode(" ", $mopt); - $if_ent[$next_if]["media"] = $mopt_list[0]; - $if_ent[$next_if]["mediaopt"] = $mopt_list[1]; - break; - } - } - // If we did not find a match return error - if (!$media_found) { - $api_resp["return"] = 3007; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Check for our description value - if (isset($client_params["descr"])) { - $descr = sanitize_str($client_params["descr"]); - // Check that is interface descriptive name does not alrady exist - if (!get_pfsense_if_id($descr)) { - $if_ent[$next_if]["descr"] = $descr; - } else { - $api_resp["return"] = 3008; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $descr = strtoupper($next_if); - $if_ent[$next_if]["descr"] = $descr; - } - // Check for our block private IP value - if (isset($client_params["blockpriv"])) { - $block_priv = true; - $if_ent[$next_if]["blockpriv"] = ""; - return $api_resp; - } - // Check for our block private IP value - if (isset($client_params["blockbogons"])) { - $block_bogons = true; - $if_ent[$next_if]["blockbogons"] = ""; - } - // Check if we have an IPv4 configuration - if (isset($client_params["type"])) { - $type = $client_params["type"]; - // Check if our IPv4 config type is allowed - if (in_array($type, $allowed_ip4_types)) { - // Gather input for our various IPv4 interface configuration types - // IPv4 STATICV4 TYPE - if ($type === "staticv4") { - // Check if our IP is set - if (isset($client_params["ipaddr"])) { - $ipaddr = $client_params["ipaddr"]; - // Check if IP address is valid - if (!is_ipaddrv4($ipaddr)) { - $api_resp["return"] = 3010; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (is_ip_in_use($ipaddr)) { - $api_resp["return"] = 3009; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $if_ent[$next_if]["ipaddr"] = $ipaddr; - } - } else { - $api_resp["return"] = 3011; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our subnet is valid - if (isset($client_params["subnet"])) { - $subnet = strval($client_params["subnet"]); - // Check if our subnet is within range - if (!is_subnet($ipaddr."/".$subnet)) { - $api_resp["return"] = 3012; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $if_ent[$next_if]["subnet"] = $subnet; - } - } else { - // Update our message if we did not already encounter an error - $api_resp["return"] = 3013; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if user specified a network gateway, if so check if it's valid - if (isset($client_params["gateway"])) { - $gateway = $client_params["gateway"]; - // Check if this gateway exists - if (!is_gateway($gateway)) { - $api_resp["return"] = 3014; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $if_ent[$next_if]["gateway"] = $gateway; - } - } - // IPv4 DHCP TYPE - } elseif ($type === "dhcp") { - $if_ent[$next_if]["ipaddr"] = $type; // Set our ipaddr value to dhcp - // Check if we have a dhcphostname value - if (isset($client_params["dhcphostname"])) { - $if_ent[$next_if]["dhcphostname"] = strval($client_params["dhcphostname"]); - } - // Check if we have a alias-address value - if (isset($client_params["alias-address"])) { - if (is_ipaddrv4($client_params["alias-address"])) { - $if_ent[$next_if]["alias-address"] = strval($client_params["alias-address"]); - if (isset($client_params["alias-subnet"])) { - $dhcpaliasnet = str($client_params["alias-subnet"]); - if (is_subnet($if_ent[$next_if]["alias-address"]."/".$dhcpaliasnet)) { - $if_ent[$next_if]["alias-subnet"] = $dhcpaliasnet; - } - } else { - $if_ent[$next_if]["alias-subnet"] = 32; - } - } else { - $api_resp["return"] = 3015; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Check if we have a dhcprejectfrom value - if (isset($client_params["dhcprejectfrom"])) { - $dhcpreject = $client_params["dhcprejectfrom"]; - // Check what data type was passed in - if (is_string($dhcpreject)) { - $dhcprejectlist = explode(",", $dhcpreject); - // Loop through our reject list and ensure values are valid - foreach ($dhcprejectlist as $ra) { - if (!is_ipaddrv4($ra)) { - $bad_reject = true; - break; - } - } - } elseif (is_array($dhcpreject)) { - // Loop through our reject list and ensure values are valid - foreach ($dhcpreject as $ra) { - if (!is_ipaddrv4($ra)) { - $bad_reject = true; - break; - } - } - // Convert our list to comma separated string - $dhcpreject = implode(",", $dhcpreject); - } - // Check for bad IPs - if ($bad_reject) { - $api_resp["return"] = 3016; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $if_ent[$next_if]["dhcprejectfrom"] = $dhcpreject; - } - } - // Check for our DHCP protocol timing - $timing_protocols = array( - "adv_dhcp_pt_timeout" => ["keyword" => "timeout", "return" => 134, "min" => 1], - "adv_dhcp_pt_retry" => ["keyword" => "retry", "return" => 135, "min" => 1], - "adv_dhcp_pt_select_timeout" => ["keyword" => "select timeout", "return" => 136, "min" => 0], - "adv_dhcp_pt_reboot" => ["keyword" => "reboot", "return" => 137, "min" => 1], - "adv_dhcp_pt_backoff_cutoff" => ["keyword" => "backoff cutoff", "return" => 138, "min" => 1], - "adv_dhcp_pt_initial_interval" => ["keyword" => "initial interval", "return" => 139, "min" => 1], - ); - // Loop through each timing attribute and see if it's valid - foreach ($timing_protocols as $tp => $data) { - if (isset($client_params[$tp])) { - // Check that value is in range - $dhcp_attr = intval($client_params[$tp]); - if ($dhcp_attr >= $data["min"]) { - $if_ent[$next_if][$tp] = $dhcp_attr; - $if_ent[$next_if]["adv_dhcp_pt_values"] = "SavedCfg"; - } else { - if ($data["keyword"] === "timeout") { - $api_resp["return"] = 3017; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif ($data["keyword"] === "retry") { - $api_resp["return"] = 3018; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif ($data["keyword"] === "select timeout") { - $api_resp["return"] = 3019; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif ($data["keyword"] === "reboot") { - $api_resp["return"] = 3020; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif ($data["keyword"] === "backoff cutoff") { - $api_resp["return"] = 3021; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif ($data["keyword"] === "initial interval") { - $api_resp["return"] = 3022; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - } - // Check for advance DHCP config - if (isset($client_params["adv_dhcp_config_advanced"])) { - $if_ent[$next_if]["adv_dhcp_config_advanced"] = "yes"; - // Check for our DHCP options - $dhcp_opts = array( - "adv_dhcp_send_options", - "adv_dhcp_request_options", - "adv_dhcp_required_options", - "adv_dhcp_option_modifiers" - ); - foreach ($dhcp_opts as $do) { - // Check if option exists - if (isset($client_params[$do])) { - $if_ent[$next_if][$do] = strval($client_params[$do]); - } - } - } - // Check for DHCP configuration file override option - if (isset($client_params["adv_dhcp_config_file_override"])) { - $if_ent[$next_if]["adv_dhcp_config_file_override"] = ""; - // Check if a file path was given - if (isset($client_params["adv_dhcp_config_file_override_path"])) { - $dhcp_conf_file = $client_params["adv_dhcp_config_file_override_path"]; - // Check that our conf file exists - if (is_file($dhcp_conf_file)) { - $if_ent[$next_if]["adv_dhcp_config_file_override"] = $dhcp_conf_file; - } else { - $api_resp["return"] = 3023; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - // Check for DHCP VLAN priority - $dhcp_vlan_prios = array( - 0 => "bk", - 1 => "be", - 2 => "ee", - 3 => "ca", - 4 => "vi", - 5 => "vo", - 6 => "ic", - 7 => "nc" - ); - if (isset($client_params["dhcpvlanenable"])) { - $if_ent[$next_if]["dhcpvlanenable"] = ""; - if (isset($client_params["dhcpcvpt"])) { - $vlan_prio = strtolower($client_params["dhcpcvpt"]); - // Check if VLAN priority was provided as number - if (is_numeric($vlan_prio) and array_key_exists(intval($vlan_prio), $dhcp_vlan_prios)) { - $if_ent[$next_if]["dhcpcvpt"] = $dhcp_vlan_prios[intval($vlan_prio)]; - } else { - // Loop through our priorities and see if value matches - foreach ($dhcp_vlan_prios as $dvp => $dvpval) { - if ($vlan_prio === $dvpval) { - $vlan_prio_found = true; - $if_ent[$next_if]["dhcpcvpt"] = $dvpval; - break; - } - } - // Check that we found a value in our loop - if (!$vlan_prio_found) { - $api_resp["return"] = 3024; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - } - } - } else { - $api_resp["return"] = 3025; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Check if we have an IPv6 configuration - if (isset($client_params["type6"])) { - $type6 = $client_params["type6"]; - // Check if our IPv6 config type is allowed - if (in_array($type6, $allowed_ip6_types)) { - // Gather input for our various IPv6 interface configuration types - // IPv6 STATICV6 TYPE - if ($type6 === "staticv6") { - // Check if our IP is set - if (isset($client_params["ipaddrv6"])) { - $ipaddrv6 = $client_params["ipaddrv6"]; - // Check if IP address is valid - if (!is_ipaddrv6($ipaddrv6)) { - $api_resp["return"] = 3026; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } elseif (is_ip_in_use($ipaddrv6)) { - $api_resp["return"] = 3027; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $if_ent[$next_if]["ipaddrv6"] = $ipaddrv6; - } - } else { - $api_resp["return"] = 3028; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if our subnet is valid - if (isset($client_params["subnetv6"])) { - $subnetv6 = strval($client_params["subnetv6"]); - // Check if our subnet is within range - if (!is_subnet($ipaddrv6 . "/" . $subnetv6)) { - $api_resp["return"] = 3029; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $if_ent[$next_if]["subnetv6"] = $subnetv6; - } - } else { - $api_resp["return"] = 3030; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check if user specified a network gateway, if so check if it's valid - if (isset($client_params["gatewayv6"])) { - $gatewayv6 = $client_params["gatewayv6"]; - // Check if this gateway exists - if (!is_gateway($gatewayv6)) { - $api_resp["return"] = 3031; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } else { - $if_ent[$next_if]["gatewayv6"] = $gatewayv6; - } - } - // Check if user set ipv6usev4iface value - if (isset($client_params["ipv6usev4iface"])) { - $if_ent[$next_if]["ipv6usev4iface"] = ""; - } - // IPv6 DHCP6 TYPE - } elseif ($type6 === "dhcp6") { - $if_ent[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to dhcp6 - // Check if user set ipv6usev4iface value - if (isset($client_params["ipv6usev4iface"])) { - $if_ent[$next_if]["ipv6usev4iface"] = ""; - } - // Check if user set dhcp6prefixonly value - if (isset($client_params["dhcp6prefixonly"])) { - $if_ent[$next_if]["dhcp6prefixonly"] = ""; - } - // Check if user set dhcp6-ia-pd-send-hint value - if (isset($client_params["dhcp6-ia-pd-send-hint"])) { - $if_ent[$next_if]["dhcp6-ia-pd-send-hint"] = ""; - } - // Check if user set dhcp6debug value - if (isset($client_params["dhcp6debug"])) { - $if_ent[$next_if]["dhcp6debug"] = ""; - } - // Check if user set dhcp6withoutra value - if (isset($client_params["dhcp6withoutra"])) { - $if_ent[$next_if]["dhcp6withoutra"] = ""; - } - // Check if user set dhcp6norelease value - if (isset($client_params["dhcp6norelease"])) { - $if_ent[$next_if]["dhcp6norelease"] = ""; - } - // Check if user set dhcp6vlanenable value - if (isset($client_params["dhcp6vlanenable"])) { - $if_ent[$next_if]["dhcp6vlanenable"] = ""; - } - // Check if user set dhcp6-ia-pd-len value - if (isset($client_params["dhcp6-ia-pd-len"])) { - // Set array of allowed prefix delegation sizes and their config translation - $dhcp6_del_size = intval($client_params["dhcp6-ia-pd-len"]); - $allowed_size = array( - 64 => 0, - 63 => 1, - 62 => 2, - 61 => 3, - 60 => 4, - 59 => 5, - 56 => 8, - 52 => 12, - 48 => 16 - ); - if (array_key_exists($dhcp6_del_size, $allowed_size)) { - $if_ent[$next_if]["dhcp6-ia-pd-len"] = $allowed_size[$dhcp6_del_size]; - } else { - $api_resp["return"] = 3032; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Check for DHCP VLAN priority - $dhcp_vlan_prios = array( - 0 => "bk", - 1 => "be", - 2 => "ee", - 3 => "ca", - 4 => "vi", - 5 => "vo", - 6 => "ic", - 7 => "nc" - ); - if (isset($client_params["dhcp6vlanenable"])) { - $if_ent[$next_if]["dhcp6vlanenable"] = ""; - if (isset($client_params["dhcp6cvpt"])) { - $vlan_prio = strtolower($client_params["dhcp6cvpt"]); - // Check if VLAN priority was provided as number - if (is_numeric($vlan_prio) and array_key_exists(intval($vlan_prio), $dhcp_vlan_prios)) { - $if_ent[$next_if]["dhcp6cvpt"] = $dhcp_vlan_prios[intval($vlan_prio)]; - } else { - // Loop through our priorities and see if value matches - foreach ($dhcp_vlan_prios as $dvp => $dvpval) { - if ($vlan_prio === $dvpval) { - $vlan_prio_found = true; - $if_ent[$next_if]["dhcp6cvpt"] = $dvpval; - break; - } - } - // Check that we found a value in our loop - if (!$vlan_prio_found) { - $api_resp["return"] = 3033; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - } - // Check for DHCP configuration file override option - if (isset($client_params["adv_dhcp6_config_file_override"])) { - $if_ent[$next_if]["adv_dhcp6_config_file_override"] = ""; - // Check if a file path was given - if (isset($client_params["adv_dhcp6_config_file_override_path"])) { - $dhcp_conf_file = $client_params["adv_dhcp6_config_file_override_path"]; - // Check that our conf file exists - if (is_file($dhcp_conf_file)) { - $if_ent[$next_if]["adv_dhcp6_config_file_override_path"] = $dhcp_conf_file; - } else { - $api_resp["return"] = 3034; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - } - // IPv6 SLAAC TYPE - } elseif ($type6 === "slaac") { - $if_ent[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to slaac - // IPv6 6RD TYPE - } elseif ($type6 === "6rd") { - $if_ent[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to 6rd - $if_ent[$next_if]["prefix-6rd-v4plen"] = $client_params["prefix-6rd-v4plen"]; // Default prefix len - // Check for a 6RD border relay - if (isset($client_params["gateway-6rd"])) { - $gw6rd = $client_params["gateway-6rd"]; - // Check that our gateway is a valid IPv4 address - if (is_ipaddrv4($gw6rd)) { - $if_ent[$next_if]["gateway-6rd"] = $client_params["gateway-6rd"]; - } else { - $api_resp["return"] = 3035; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 3036; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check for a 6RD prefix - if (isset($client_params["prefix-6rd"])) { - $if_ent[$next_if]["prefix-6rd"] = $client_params["prefix-6rd"]; - } - // Check for a 6RD prefix length - if (isset($client_params["prefix-6rd-v4plen"])) { - $prefix_len = $client_params["prefix-6rd-v4plen"]; - // Check if our prefix length is within range - if (is_numeric($prefix_len) and (0 <= intval($prefix_len) and intval($prefix_len) <= 32)) { - $if_ent[$next_if]["prefix-6rd-v4plen"] = $client_params["prefix-6rd-v4plen"]; - } else { - $api_resp["return"] = 3037; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // IPv6 TRACK TYPE - } elseif ($type6 === "track6") { - $if_ent[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to track6 - // Check for track 6 interface - if (isset($client_params["track6-interface"])) { - $track_if = $client_params["track6-interface"]; - $track_if = get_pfsense_if_id($track_if); - // Check that our gateway is a valid IPv4 address - if (array_key_exists($track_if, get_ipv6_if_list())) { - $if_ent[$next_if]["track6-interface"] = $client_params["track6-interface"]; - } else { - $api_resp["return"] = 3038; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp["return"] = 3039; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - // Check for track 6 prefix ID - $track_prefix = 0; // Default our prefix value - if (isset($client_params["track6-prefix-id-hex"])) { - $track_prefix = $client_params["track6-prefix-id-hex"]; - // Check that our gateway is a valid IPv4 address - if (is_numeric($track_prefix) and ctype_xdigit(strval($track_prefix))) { - $if_ent[$next_if]["track6-prefix-id--hex"] = intval($track_prefix); - } else { - $api_resp["return"] = 3040; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // IPv6 6-to-4 TYPE - } elseif ($type6 === "6to4") { - $if_ent[$next_if]["ipaddrv6"] = $type6; // Set our ipaddrv6 value to 6to4 - } - } else { - $api_resp["return"] = 3041; - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } - // Add debug data if requested - if (array_key_exists("debug", $client_params)) { - echo "PHYSICAL INTERFACE:" . PHP_EOL; - echo var_dump($interface) . PHP_EOL; - echo "pfSENSE INTERFACE ID:" . PHP_EOL; - echo var_dump($next_if) . PHP_EOL; - echo "ENABLE:" . PHP_EOL; - echo var_dump($enable) . PHP_EOL; - echo "CUSTOM MAC ADDRESS:".PHP_EOL; - echo var_dump($mac_addr).PHP_EOL; - echo "MTU:" . PHP_EOL; - echo var_dump($mtu) . PHP_EOL; - echo "MSS:" . PHP_EOL; - echo var_dump($mss) . PHP_EOL; - echo "DESCRIPTIVE NAME:" . PHP_EOL; - echo var_dump($descr) . PHP_EOL; - echo "IPv4 CONFIGURATION TYPE".PHP_EOL; - echo var_dump($type).PHP_EOL; - echo "IPv4 CONFIGURATION TYPE".PHP_EOL; - echo var_dump($type6).PHP_EOL; - echo "IP CONFIGURATION".PHP_EOL; - echo var_dump($if_ent).PHP_EOL; - } - // Apply our configuration - $_SESSION["Username"] = $client_id; // Save our CLIENT ID to session data for logging - $change_note = " Added interface via API"; // Add a change note - $config["interfaces"] = $config["interfaces"] + $if_ent; // Add our new interface config - write_config(sprintf(gettext($change_note))); // Write our configuration change - $apply_if = apply_interface_config($if_ent); - if ($apply_if) { - $api_resp = array("status" => "ok", "code" => 200, "return" => 0, "message" => "interface added"); - $api_resp["data"] = $if_ent; - return $api_resp; - } else { - // Return error response if our loop did not find a match - $api_resp = array("status" => "server error", "code" => 500, "return" => 1); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - $api_resp = array("status" => "bad request", "code" => 400, "return" => 2); - $api_resp["message"] = $err_lib[$api_resp["return"]]; - return $api_resp; - } - } else { - return $api_resp; - } -} - diff --git a/pfSense-pkg-API/files/etc/inc/apiresp.inc b/pfSense-pkg-API/files/etc/inc/apiresp.inc deleted file mode 100644 index 48a6941f3..000000000 --- a/pfSense-pkg-API/files/etc/inc/apiresp.inc +++ /dev/null @@ -1,208 +0,0 @@ - "Success", - 1 => "Process encountered unexpected error", - 2 => "Invalid HTTP method", - 3 => "Authentication failed", - 4 => "Authorization failed", - 5 => "Incompatible pfSense version", - 6 => "Forbidden", - 7 => "Search attribute not found", - 8 => "Could not locate pfSense version", - 9 => "Authentication mode must be set to JWT to enable access token authentication", - - // 1000-1999 reserved for /system API calls - 1000 => "Invalid system hostname", - 1001 => "Invalid system hostname domain", - 1002 => "System certificate descriptive name required", - 1003 => "Invalid system certificate", - 1004 => "Invalid system certificate key", - 1005 => "System certificate in use", - 1006 => "ARP IP required", - 1007 => "Invalid system DNS server IP address", - 1008 => "System DNS server IP address required", - // 2000-2999 reserved for /services API calls - 2000 => "Invalid sshd enable value", - 2001 => "Invalid sshd key only mode", - 2002 => "Invalid sshd agent forwarding value", - 2003 => "Invalid sshd port", - 2004 => "Unbound host override hostname required", - 2005 => "Unbound host override domain required", - 2006 => "Unbound host override IP address required", - 2007 => "Unbound host override alias hostname required", - 2008 => "Unbound host override alias domain required", - 2009 => "Unbound host override alias already exists", - 2010 => "Unbound host override already exists", - 2011 => "Invalid unbound host override IP address", - 2012 => "Unbound host override new IP required", - 2013 => "Unbound host override does not exist", - // 3000-3999 reserved for /interfaces API calls - 3000 => "Unknown interface ID", - 3001 => "Interface ID in use", - 3002 => "Interface ID required", - 3003 => "Invalid interface MAC address", - 3004 => "Invalid interface MTU value", - 3005 => "Invalid interface MSS value", - 3006 => "Interface MSS value greater than parent interface", - 3007 => "Invalid interface speed and duplex", - 3008 => "Interface descriptive name already in use", - 3009 => "Interface IPv4 address already in use", - 3010 => "Invalid interface IPv4 address", - 3011 => "Interface type staticv4 requires IPv4 address", - 3012 => "Invalid interface IPv4 subnet", - 3013 => "Interface type staticv4 requires subnet bitmask", - 3014 => "Invalid interface IPv4 gateway", - 3015 => "Invalid interface DHCP alias address", - 3016 => "Invalid interface DHCP reject from address", - 3017 => "Invalid interface DHCP timeout value", - 3018 => "Invalid interface DHCP retry value", - 3019 => "Invalid interface DHCP timeout value", - 3020 => "Invalid interface DHCP reboot value", - 3021 => "Invalid interface DHCP backoff cutoff value", - 3022 => "Invalid interface DHCP initial interval value", - 3023 => "Invalid interface DHCP configuration file", - 3024 => "Invalid interface DHCP VLAN priority value", - 3025 => "Invalid interface IPv4 configuration type", - 3026 => "Invalid interface IPv6 address", - 3027 => "Interface IPv6 address already in use", - 3028 => "Interface staticv6 requires IPv6 address", - 3029 => "Invalid interface IPv6 subnet", - 3030 => "Interface type staticv6 requires subnet bitmask", - 3031 => "Invalid interface IPv6 gateway", - 3032 => "Invalid interface IPv6 prefix delegation size", - 3033 => "Invalid interface DHCP6 VLAN priority value", - 3034 => "Invalid interface DHCP6 configuration file", - 3035 => "Invalid interface 6RD gateway address", - 3036 => "Interface 6RD gateway address required", - 3037 => "Invalid interface 6RD prefix length", - 3038 => "Invalid interface IPv6 track interface", - 3039 => "Interface IPv6 track interface required", - 3040 => "Invalid interface IPv6 track prefix hex value", - 3041 => "Invalid interface IPv6 configuration type", - 3042 => "Interface WAN cannot be deleted", - 3043 => "Interface cannot be deleted while member of interface group", - 3044 => "Interface cannot be deleted while member of bridge interface", - 3045 => "Interface cannot be deleted while member of GRE tunnel", - 3046 => "Interface cannot be deleted while member of GIF tunnel", - 3047 => "Interface cannot be deleted with existing traffic shaper configuration", - 3048 => "Interface VLAN tag required", - 3049 => "Interface VLAN tag in use", - 3050 => "Interface VLAN does not exist", - 3051 => "Interface VLAN parent does not exist", - 3052 => "Invalid interface VLAN tag", - 3053 => "Invalid interface VLAN priority value", - 3054 => "Interface VLAN already exists on parent", - 3055 => "Interface VLAN parent required", - // 4000-4999 reserved for /firewall API calls - 4000 => "Port forward interface required", - 4001 => "Port forward rule protocol required", - 4002 => "Port forward redirect IP required", - 4003 => "Port forward redirect port required", - 4004 => "Port forward source required", - 4005 => "Port forward destination required", - 4006 => "Unknown port forward interface", - 4007 => "Unknown port forward protocol", - 4008 => "Unknown port forward NAT reflection value", - 4009 => "Invalid port forward redirect IP", - 4010 => "Invalid port forward redirect port", - 4011 => "Invalid port forward source address", - 4012 => "Invalid port forward source address", - 4013 => "Invalid port forward source port", - 4014 => "Invalid port forward destination port", - 4015 => "Port forward rule ID required", - 4016 => "Port forward rule ID does not exist", - 4017 => "Virtual IP configuration ID required", - 4018 => "Virtual IP configuration ID does not exist", - 4019 => "Virtual IP mode required", - 4020 => "Virtual IP interface required", - 4021 => "Virtual IP subnet required", - 4022 => "Virtual IP CARP password required", - 4023 => "Unknown virtual IP mode", - 4024 => "Unknown virtual IP interface", - 4025 => "Invalid virtual IP subnet", - 4026 => "Virtual IP subnet already in use", - 4027 => "Virtual IP VHID already in use", - 4028 => "Invalid virtual IP VHID", - 4029 => "Invalid virtual IP CARP advertisement base", - 4030 => "Invalid virtual IP CARP advertisement skew", - 4031 => "Firewall rule ID required", - 4032 => "Firewall rule ID does not exist", - 4033 => "Firewall rule type required", - 4034 => "Firewall rule interface required", - 4035 => "Firewall rule IP version required", - 4036 => "Firewall rule protocol required", - 4037 => "Firewall rule source required", - 4038 => "Firewall rule destination required", - 4039 => "Unknown firewall rule type", - 4040 => "Unknown firewall rule interface", - 4041 => "Unknown firewall rule IP version", - 4042 => "Unknown firewall rule protocol", - 4043 => "Unknown firewall rule gateway", - 4044 => "Invalid firewall rule source", - 4045 => "Invalid firewall rule destination", - 4046 => "Unknown firewall rule ICMP subtype", - 4047 => "Firewall rule source and destination port required", - 4048 => "Invalid firewall rule source port", - 4049 => "Invalid firewall rule destination port", - 4050 => "Firewall alias name required", - 4051 => "Firewall alias in use", - 4052 => "Firewall alias address value required", - 4053 => "Firewall alias name must be type string", - 4054 => "Firewall alias address must be type array", - 4055 => "Firewall alias does not exist", - 4056 => "Firewall alias already exists", - 4057 => "Unknown firewall alias type", - 4058 => "Invalid firewall alias IP address or hostname", - 4059 => "Invalid firewall alias subnet", - 4060 => "Invalid firewall alias port", - 4061 => "Firewall alias type required", - 4062 => "Firewall alias type must be type string", - 4063 => "Firewall alias description must be type string", - 4064 => "Firewall alias detail must be type array", - 4065 => "Invalid firewall alias port value", - 4066 => "Firewall alias port values must be numeric", - 4067 => "Invalid firewall alias IP address", - 4068 => "Invalid firewall alias subnet", - 4069 => "Firewall alias address values must be valid CIDR", - 4070 => "Alias address contents must be type string", - 4071 => "Alias detail contents must be type string", - 4072 => "Firewall state maximum size required", - 4073 => "Invalid firewall state table size", - //5000-5999 reserved for /users API calls - 5000 => "Username required", - 5001 => "User does not exist", - 5002 => "User already exists", - 5003 => "User password required", - 5004 => "User privilege required", - 5005 => "User privilege must be type array or string", - 5006 => "Unknown user privilege", - 5007 => "User group name required", - 5008 => "Unknown group name", - 5009 => "Group must be type array or string", - 5010 => "User authentication server name required", - 5011 => "LDAP authentication server hostname or IP required", - 5012 => "Invalid LDAP authentication server hostname or IP", - 5013 => "Authentication server LDAP port required", - 5014 => "Invalid authentication server LDAP port", - 5015 => "Authentication server LDAP URL type required", - 5016 => "Invalid authentication server LDAP URL type", - 5017 => "Invalid authentication server LDAP protocol version", - 5018 => "Authentication server LDAP search scope required", - 5019 => "Invalid authentication server LDAP search scope", - 5020 => "Authentication server LDAP bind DN required", - 5021 => "Authentication server LDAP bind password required", - 5022 => "Authentication server LDAP user naming attribute required", - 5023 => "Authentication server LDAP group naming attribute required", - 5024 => "Authentication server LDAP group member attribute required", - ); - return $err_lib; -} - -// Export the error array as JSON for third party integration -function export_err_lib() { - $err_lib = api_error_lib(); - return json_encode($err_lib); -} \ No newline at end of file diff --git a/pfSense-pkg-API/files/pkg-deinstall.in b/pfSense-pkg-API/files/pkg-deinstall.in index 84f5119cd..8f4c81886 100644 --- a/pfSense-pkg-API/files/pkg-deinstall.in +++ b/pfSense-pkg-API/files/pkg-deinstall.in @@ -1,3 +1,2 @@ #!/bin/sh -/bin/rm -rf /etc/inc/php-jwt /usr/local/bin/php -f /etc/rc.packages %%PORTNAME%% ${2} \ No newline at end of file diff --git a/pfSense-pkg-API/pkg-plist b/pfSense-pkg-API/pkg-plist index 9b7b54bb4..7d5bb15ad 100644 --- a/pfSense-pkg-API/pkg-plist +++ b/pfSense-pkg-API/pkg-plist @@ -1,213 +1,172 @@ -@dir /etc/inc -@dir /etc/inc/php-jwt -@dir /etc/inc/php-jwt/src -/etc/inc/api.inc -/etc/inc/apicalls.inc -/etc/inc/apiresp.inc -/etc/inc/php-jwt/README.md -/etc/inc/php-jwt/LICENSE -/etc/inc/php-jwt/composer.json -/etc/inc/php-jwt/src/JWK.php -/etc/inc/php-jwt/src/JWT.php -/etc/inc/php-jwt/src/SignatureInvalidException.php -/etc/inc/php-jwt/src/ExpiredException.php -/etc/inc/php-jwt/src/BeforeValidException.php - +@dir /usr/local +@dir /usr/local/www @dir /usr/local/pkg -/usr/local/pkg/api.xml - +@dir /usr/local/share @dir /usr/local/www/api @dir /usr/local/www/api/v1 -/usr/local/www/api/index.php - -@dir /usr/local/www/api/v1/access_token -/usr/local/www/api/v1/access_token/index.php - +@dir /usr/local/www/api/v1/status +@dir /usr/local/www/api/v1/firewall +@dir /usr/local/www/api/v1/system @dir /usr/local/www/api/v1/users -@dir /usr/local/www/api/v1/users/add +@dir /usr/local/www/api/v1/routing +@dir /usr/local/www/api/v1/access_token +@dir /usr/local/www/api/v1/services +@dir /usr/local/www/api/v1/interfaces +@dir /usr/local/www/api/v1/status/carp +@dir /usr/local/www/api/v1/status/carp/modify +@dir /usr/local/www/api/v1/firewall/nat +@dir /usr/local/www/api/v1/firewall/virtualips +@dir /usr/local/www/api/v1/firewall/states +@dir /usr/local/www/api/v1/firewall/rules +@dir /usr/local/www/api/v1/firewall/aliases +@dir /usr/local/www/api/v1/firewall/nat/portforwards +@dir /usr/local/www/api/v1/firewall/nat/portforwards/delete +@dir /usr/local/www/api/v1/firewall/nat/portforwards/add +@dir /usr/local/www/api/v1/firewall/virtualips/delete +@dir /usr/local/www/api/v1/firewall/virtualips/add +@dir /usr/local/www/api/v1/firewall/states/size +@dir /usr/local/www/api/v1/firewall/states/size/modify +@dir /usr/local/www/api/v1/firewall/rules/delete +@dir /usr/local/www/api/v1/firewall/rules/add +@dir /usr/local/www/api/v1/firewall/aliases/delete +@dir /usr/local/www/api/v1/firewall/aliases/modify +@dir /usr/local/www/api/v1/firewall/aliases/add +@dir /usr/local/www/api/v1/firewall/aliases/delete/address +@dir /usr/local/www/api/v1/firewall/aliases/add/address +@dir /usr/local/www/api/v1/system/config +@dir /usr/local/www/api/v1/system/certificates +@dir /usr/local/www/api/v1/system/version +@dir /usr/local/www/api/v1/system/api +@dir /usr/local/www/api/v1/system/hostname +@dir /usr/local/www/api/v1/system/arp +@dir /usr/local/www/api/v1/system/dns +@dir /usr/local/www/api/v1/system/certificates/delete +@dir /usr/local/www/api/v1/system/certificates/add +@dir /usr/local/www/api/v1/system/api/errors +@dir /usr/local/www/api/v1/system/hostname/modify +@dir /usr/local/www/api/v1/system/arp/delete +@dir /usr/local/www/api/v1/system/dns/delete +@dir /usr/local/www/api/v1/system/dns/modify +@dir /usr/local/www/api/v1/system/dns/delete/servers @dir /usr/local/www/api/v1/users/delete @dir /usr/local/www/api/v1/users/modify -@dir /usr/local/www/api/v1/users/add/privs +@dir /usr/local/www/api/v1/users/authservers +@dir /usr/local/www/api/v1/users/add @dir /usr/local/www/api/v1/users/delete/privs -@dir /usr/local/www/api/v1/users/add/groups @dir /usr/local/www/api/v1/users/delete/groups -@dir /usr/local/www/api/v1/users/authservers @dir /usr/local/www/api/v1/users/authservers/delete @dir /usr/local/www/api/v1/users/authservers/ldap -@dir /usr/local/www/api/v1/users/authservers/ldap/add -@dir /usr/local/www/api/v1/users/authservers/ldap/delete @dir /usr/local/www/api/v1/users/authservers/radius +@dir /usr/local/www/api/v1/users/authservers/ldap/delete +@dir /usr/local/www/api/v1/users/authservers/ldap/add @dir /usr/local/www/api/v1/users/authservers/radius/delete -/usr/local/www/api/v1/users/index.php -/usr/local/www/api/v1/users/add/index.php -/usr/local/www/api/v1/users/delete/index.php -/usr/local/www/api/v1/users/modify/index.php -/usr/local/www/api/v1/users/add/privs/index.php -/usr/local/www/api/v1/users/delete/privs/index.php -/usr/local/www/api/v1/users/add/groups/index.php -/usr/local/www/api/v1/users/delete/groups/index.php -/usr/local/www/api/v1/users/authservers/index.php -/usr/local/www/api/v1/users/authservers/delete/index.php -/usr/local/www/api/v1/users/authservers/ldap/index.php -/usr/local/www/api/v1/users/authservers/ldap/add/index.php -/usr/local/www/api/v1/users/authservers/ldap/delete/index.php -/usr/local/www/api/v1/users/authservers/radius/index.php -/usr/local/www/api/v1/users/authservers/radius/delete/index.php - -@dir /usr/local/www/api/v1/system/version -/usr/local/www/api/v1/system/version/index.php - -@dir /usr/local/www/api/v1/system/api -@dir /usr/local/www/api/v1/system/api/errors -/usr/local/www/api/v1/system/api/index.php -/usr/local/www/api/v1/system/api/errors/index.php - -@dir /usr/local/www/api/v1/system/arp -@dir /usr/local/www/api/v1/system/arp/delete -/usr/local/www/api/v1/system/arp/index.php -/usr/local/www/api/v1/system/arp/delete/index.php - -@dir /usr/local/www/api/v1/system/config -/usr/local/www/api/v1/system/config/index.php - -@dir /usr/local/www/api/v1/system/hostname -@dir /usr/local/www/api/v1/system/hostname/modify -/usr/local/www/api/v1/system/hostname/index.php -/usr/local/www/api/v1/system/hostname/modify/index.php - -@dir /usr/local/www/api/v1/system/dns -@dir /usr/local/www/api/v1/system/dns/modify -@dir /usr/local/www/api/v1/system/dns/delete -@dir /usr/local/www/api/v1/system/dns/delete/servers -/usr/local/www/api/v1/system/dns/index.php -/usr/local/www/api/v1/system/dns/modify/index.php -/usr/local/www/api/v1/system/dns/delete/servers/index.php - -@dir /usr/local/www/api/v1/system/certificates -@dir /usr/local/www/api/v1/system/certificates/add -@dir /usr/local/www/api/v1/system/certificates/delete -/usr/local/www/api/v1/system/certificates/index.php -/usr/local/www/api/v1/system/certificates/add/index.php -/usr/local/www/api/v1/system/certificates/delete/index.php - -@dir /usr/local/www/api/v1/status -@dir /usr/local/www/api/v1/status/carp -@dir /usr/local/www/api/v1/status/carp/modify -/usr/local/www/api/v1/status/carp/index.php -/usr/local/www/api/v1/status/carp/modify/index.php - -@dir /usr/local/www/api/v1/routing +@dir /usr/local/www/api/v1/users/add/privs +@dir /usr/local/www/api/v1/users/add/groups @dir /usr/local/www/api/v1/routing/gateways -/usr/local/www/api/v1/routing/gateways/index.php - -@dir /usr/local/www/api/v1/interfaces -@dir /usr/local/www/api/v1/interfaces/add -@dir /usr/local/www/api/v1/interfaces/delete -@dir /usr/local/www/api/v1/interfaces/vlans -@dir /usr/local/www/api/v1/interfaces/vlans/add -@dir /usr/local/www/api/v1/interfaces/vlans/delete -@dir /usr/local/www/api/v1/interfaces/vlans/modify -/usr/local/www/api/v1/interfaces/index.php -/usr/local/www/api/v1/interfaces/add/index.php -/usr/local/www/api/v1/interfaces/delete/index.php -/usr/local/www/api/v1/interfaces/vlans/index.php -/usr/local/www/api/v1/interfaces/vlans/add/index.php -/usr/local/www/api/v1/interfaces/vlans/delete/index.php -/usr/local/www/api/v1/interfaces/vlans/modify/index.php - - - -@dir /usr/local/www/api/v1/firewall - -@dir /usr/local/www/api/v1/firewall/states -@dir /usr/local/www/api/v1/firewall/states/size -@dir /usr/local/www/api/v1/firewall/states/size/modify -/usr/local/www/api/v1/firewall/states/index.php -/usr/local/www/api/v1/firewall/states/size/index.php -/usr/local/www/api/v1/firewall/states/size/modify/index.php - -@dir /usr/local/www/api/v1/firewall/virtualips -@dir /usr/local/www/api/v1/firewall/virtualips/add -@dir /usr/local/www/api/v1/firewall/virtualips/delete -/usr/local/www/api/v1/firewall/virtualips/index.php -/usr/local/www/api/v1/firewall/virtualips/add/index.php -/usr/local/www/api/v1/firewall/virtualips/delete/index.php - -@dir /usr/local/www/api/v1/firewall/nat -@dir /usr/local/www/api/v1/firewall/nat/portforwards -@dir /usr/local/www/api/v1/firewall/nat/portforwards/add -@dir /usr/local/www/api/v1/firewall/nat/portforwards/delete -/usr/local/www/api/v1/firewall/nat/index.php -/usr/local/www/api/v1/firewall/nat/portforwards/index.php -/usr/local/www/api/v1/firewall/nat/portforwards/add/index.php -/usr/local/www/api/v1/firewall/nat/portforwards/delete/index.php - -@dir /usr/local/www/api/v1/firewall/aliases -@dir /usr/local/www/api/v1/firewall/aliases/add -@dir /usr/local/www/api/v1/firewall/aliases/delete -@dir /usr/local/www/api/v1/firewall/aliases/modify -@dir /usr/local/www/api/v1/firewall/aliases/add/address -@dir /usr/local/www/api/v1/firewall/aliases/delete/address -/usr/local/www/api/v1/firewall/aliases/index.php -/usr/local/www/api/v1/firewall/aliases/add/index.php -/usr/local/www/api/v1/firewall/aliases/delete/index.php -/usr/local/www/api/v1/firewall/aliases/modify/index.php -/usr/local/www/api/v1/firewall/aliases/add/address/index.php -/usr/local/www/api/v1/firewall/aliases/delete/address/index.php - -@dir /usr/local/www/api/v1/firewall/rules -@dir /usr/local/www/api/v1/firewall/rules/add -@dir /usr/local/www/api/v1/firewall/rules/delete -/usr/local/www/api/v1/firewall/rules/index.php -/usr/local/www/api/v1/firewall/rules/add/index.php -/usr/local/www/api/v1/firewall/rules/delete/index.php - -@dir /usr/local/www/api/v1/services +@dir /usr/local/www/api/v1/services/ntpd +@dir /usr/local/www/api/v1/services/sshd +@dir /usr/local/www/api/v1/services/syslogd @dir /usr/local/www/api/v1/services/start @dir /usr/local/www/api/v1/services/stop @dir /usr/local/www/api/v1/services/restart @dir /usr/local/www/api/v1/services/dhcpd -@dir /usr/local/www/api/v1/services/dhcpd/start -@dir /usr/local/www/api/v1/services/dhcpd/stop -@dir /usr/local/www/api/v1/services/dhcpd/restart @dir /usr/local/www/api/v1/services/dpinger -@dir /usr/local/www/api/v1/services/dpinger/start -@dir /usr/local/www/api/v1/services/dpinger/stop -@dir /usr/local/www/api/v1/services/dpinger/restart -@dir /usr/local/www/api/v1/services/ntpd +@dir /usr/local/www/api/v1/services/unbound @dir /usr/local/www/api/v1/services/ntpd/start @dir /usr/local/www/api/v1/services/ntpd/stop @dir /usr/local/www/api/v1/services/ntpd/restart -@dir /usr/local/www/api/v1/services/sshd @dir /usr/local/www/api/v1/services/sshd/modify @dir /usr/local/www/api/v1/services/sshd/start @dir /usr/local/www/api/v1/services/sshd/stop @dir /usr/local/www/api/v1/services/sshd/restart -@dir /usr/local/www/api/v1/services/syslogd @dir /usr/local/www/api/v1/services/syslogd/start @dir /usr/local/www/api/v1/services/syslogd/stop @dir /usr/local/www/api/v1/services/syslogd/restart -@dir /usr/local/www/api/v1/services/unbound -@dir /usr/local/www/api/v1/services/unbound/add +@dir /usr/local/www/api/v1/services/dhcpd/start +@dir /usr/local/www/api/v1/services/dhcpd/stop +@dir /usr/local/www/api/v1/services/dhcpd/restart +@dir /usr/local/www/api/v1/services/dpinger/start +@dir /usr/local/www/api/v1/services/dpinger/stop +@dir /usr/local/www/api/v1/services/dpinger/restart +@dir /usr/local/www/api/v1/services/unbound/apply @dir /usr/local/www/api/v1/services/unbound/delete @dir /usr/local/www/api/v1/services/unbound/modify @dir /usr/local/www/api/v1/services/unbound/start @dir /usr/local/www/api/v1/services/unbound/stop @dir /usr/local/www/api/v1/services/unbound/restart -@dir /usr/local/www/api/v1/services/unbound/apply -@dir /usr/local/www/api/v1/services/unbound/add/hosts +@dir /usr/local/www/api/v1/services/unbound/add @dir /usr/local/www/api/v1/services/unbound/delete/hosts @dir /usr/local/www/api/v1/services/unbound/modify/hosts +@dir /usr/local/www/api/v1/services/unbound/add/hosts +@dir /usr/local/www/api/v1/interfaces/vlans +@dir /usr/local/www/api/v1/interfaces/delete +@dir /usr/local/www/api/v1/interfaces/add +@dir /usr/local/www/api/v1/interfaces/vlans/delete +@dir /usr/local/www/api/v1/interfaces/vlans/modify +@dir /usr/local/www/api/v1/interfaces/vlans/add +@dir /usr/local/share/pfSense-pkg-API +@dir /etc/inc +@dir /etc/inc/php-jwt +@dir /etc/inc/api +@dir /etc/inc/php-jwt/src +@dir /etc/inc/api/framework +@dir /etc/inc/api/api_models + +/usr/local/www/api/index.php +/usr/local/www/api/v1/api_return_codes.txt +/usr/local/www/api/v1/status/carp/index.php +/usr/local/www/api/v1/status/carp/modify/index.php +/usr/local/www/api/v1/firewall/nat/index.php +/usr/local/www/api/v1/firewall/nat/portforwards/index.php +/usr/local/www/api/v1/firewall/nat/portforwards/delete/index.php +/usr/local/www/api/v1/firewall/nat/portforwards/add/index.php +/usr/local/www/api/v1/firewall/virtualips/index.php +/usr/local/www/api/v1/firewall/virtualips/delete/index.php +/usr/local/www/api/v1/firewall/virtualips/add/index.php +/usr/local/www/api/v1/firewall/states/index.php +/usr/local/www/api/v1/firewall/states/size/index.php +/usr/local/www/api/v1/firewall/states/size/modify/index.php +/usr/local/www/api/v1/firewall/rules/index.php +/usr/local/www/api/v1/firewall/rules/delete/index.php +/usr/local/www/api/v1/firewall/rules/add/index.php +/usr/local/www/api/v1/firewall/aliases/index.php +/usr/local/www/api/v1/firewall/aliases/delete/index.php +/usr/local/www/api/v1/firewall/aliases/delete/address/index.php +/usr/local/www/api/v1/firewall/aliases/modify/index.php +/usr/local/www/api/v1/firewall/aliases/add/index.php +/usr/local/www/api/v1/firewall/aliases/add/address/index.php +/usr/local/www/api/v1/system/config/index.php +/usr/local/www/api/v1/system/certificates/index.php +/usr/local/www/api/v1/system/certificates/delete/index.php +/usr/local/www/api/v1/system/certificates/add/index.php +/usr/local/www/api/v1/system/version/index.php +/usr/local/www/api/v1/system/api/index.php +/usr/local/www/api/v1/system/api/errors/index.php +/usr/local/www/api/v1/system/hostname/index.php +/usr/local/www/api/v1/system/hostname/modify/index.php +/usr/local/www/api/v1/system/arp/index.php +/usr/local/www/api/v1/system/arp/delete/index.php +/usr/local/www/api/v1/system/dns/index.php +/usr/local/www/api/v1/system/dns/delete/servers/index.php +/usr/local/www/api/v1/system/dns/modify/index.php +/usr/local/www/api/v1/users/index.php +/usr/local/www/api/v1/users/delete/index.php +/usr/local/www/api/v1/users/delete/privs/index.php +/usr/local/www/api/v1/users/delete/groups/index.php +/usr/local/www/api/v1/users/modify/index.php +/usr/local/www/api/v1/users/authservers/index.php +/usr/local/www/api/v1/users/authservers/delete/index.php +/usr/local/www/api/v1/users/authservers/ldap/index.php +/usr/local/www/api/v1/users/authservers/ldap/delete/index.php +/usr/local/www/api/v1/users/authservers/ldap/add/index.php +/usr/local/www/api/v1/users/authservers/radius/index.php +/usr/local/www/api/v1/users/authservers/radius/delete/index.php +/usr/local/www/api/v1/users/add/index.php +/usr/local/www/api/v1/users/add/privs/index.php +/usr/local/www/api/v1/users/add/groups/index.php +/usr/local/www/api/v1/routing/gateways/index.php +/usr/local/www/api/v1/access_token/index.php /usr/local/www/api/v1/services/index.php -/usr/local/www/api/v1/services/start/index.php -/usr/local/www/api/v1/services/stop/index.php -/usr/local/www/api/v1/services/restart/index.php -/usr/local/www/api/v1/services/dhcpd/start/index.php -/usr/local/www/api/v1/services/dhcpd/stop/index.php -/usr/local/www/api/v1/services/dhcpd/restart/index.php -/usr/local/www/api/v1/services/dpinger/start/index.php -/usr/local/www/api/v1/services/dpinger/stop/index.php -/usr/local/www/api/v1/services/dpinger/restart/index.php /usr/local/www/api/v1/services/ntpd/start/index.php /usr/local/www/api/v1/services/ntpd/stop/index.php /usr/local/www/api/v1/services/ntpd/restart/index.php @@ -219,14 +178,129 @@ /usr/local/www/api/v1/services/syslogd/start/index.php /usr/local/www/api/v1/services/syslogd/stop/index.php /usr/local/www/api/v1/services/syslogd/restart/index.php +/usr/local/www/api/v1/services/start/index.php +/usr/local/www/api/v1/services/stop/index.php +/usr/local/www/api/v1/services/restart/index.php +/usr/local/www/api/v1/services/dhcpd/start/index.php +/usr/local/www/api/v1/services/dhcpd/stop/index.php +/usr/local/www/api/v1/services/dhcpd/restart/index.php +/usr/local/www/api/v1/services/dpinger/start/index.php +/usr/local/www/api/v1/services/dpinger/stop/index.php +/usr/local/www/api/v1/services/dpinger/restart/index.php /usr/local/www/api/v1/services/unbound/index.php +/usr/local/www/api/v1/services/unbound/apply/index.php +/usr/local/www/api/v1/services/unbound/delete/hosts/index.php +/usr/local/www/api/v1/services/unbound/modify/hosts/index.php /usr/local/www/api/v1/services/unbound/start/index.php /usr/local/www/api/v1/services/unbound/stop/index.php /usr/local/www/api/v1/services/unbound/restart/index.php -/usr/local/www/api/v1/services/unbound/apply/index.php /usr/local/www/api/v1/services/unbound/add/hosts/index.php -/usr/local/www/api/v1/services/unbound/delete/hosts/index.php -/usr/local/www/api/v1/services/unbound/modify/hosts/index.php - - -%%DATADIR%%/info.xml +/usr/local/www/api/v1/interfaces/index.php +/usr/local/www/api/v1/interfaces/vlans/index.php +/usr/local/www/api/v1/interfaces/vlans/delete/index.php +/usr/local/www/api/v1/interfaces/vlans/modify/index.php +/usr/local/www/api/v1/interfaces/vlans/add/index.php +/usr/local/www/api/v1/interfaces/delete/index.php +/usr/local/www/api/v1/interfaces/add/index.php +/usr/local/pkg/api.xml +/usr/local/share/pfSense-pkg-API/info.xml +/etc/inc/php-jwt/LICENSE +/etc/inc/php-jwt/README.md +/etc/inc/php-jwt/composer.json +/etc/inc/php-jwt/src/SignatureInvalidException.php +/etc/inc/php-jwt/src/BeforeValidException.php +/etc/inc/php-jwt/src/JWK.php +/etc/inc/php-jwt/src/ExpiredException.php +/etc/inc/php-jwt/src/JWT.php +/etc/inc/api/framework/APITools.inc +/etc/inc/api/framework/APIAuth.inc +/etc/inc/api/framework/APIBaseModel.inc +/etc/inc/api/framework/APIResponse.inc +/etc/inc/api/api_models/APIUsersAuthservers.inc +/etc/inc/api/api_models/APIServicesUnboundRestart.inc +/etc/inc/api/api_models/APIUsersAuthserversLDAPDelete.inc +/etc/inc/api/api_models/APIFirewallAliasesAdd.inc +/etc/inc/api/api_models/APIStatusCarpModify.inc +/etc/inc/api/api_models/APIFirewallNatPortForwardsDelete.inc +/etc/inc/api/api_models/APIFirewallVirtualIPs.inc +/etc/inc/api/api_models/APIInterfacesAdd.inc +/etc/inc/api/api_models/APIServicesUnbound.inc +/etc/inc/api/api_models/APIServicesSyslogdRestart.inc +/etc/inc/api/api_models/APISystemDNSModify.inc +/etc/inc/api/api_models/APIServicesRestart.inc +/etc/inc/api/api_models/APIServicesSSHdModify.inc +/etc/inc/api/api_models/APIServicesUnboundStop.inc +/etc/inc/api/api_models/APIServicesSSHdRestart.inc +/etc/inc/api/api_models/APIServicesNtpdStart.inc +/etc/inc/api/api_models/APIInterfacesVLANs.inc +/etc/inc/api/api_models/APIServicesSSHdStop.inc +/etc/inc/api/api_models/APIInterfacesVLANsModify.inc +/etc/inc/api/api_models/APIAccessToken.inc +/etc/inc/api/api_models/APIServicesUnboundDeleteHosts.inc +/etc/inc/api/api_models/APISystemARP.inc +/etc/inc/api/api_models/APISystemCertificates.inc +/etc/inc/api/api_models/APIServicesDpingerStop.inc +/etc/inc/api/api_models/APIFirewallRulesAdd.inc +/etc/inc/api/api_models/APISystemARPDelete.inc +/etc/inc/api/api_models/APIServicesSyslogdStop.inc +/etc/inc/api/api_models/APIUsersAuthserversLDAPAdd.inc +/etc/inc/api/api_models/APIInterfacesDelete.inc +/etc/inc/api/api_models/APIServicesSyslogdStart.inc +/etc/inc/api/api_models/APIUsersModify.inc +/etc/inc/api/api_models/APIUsersAuthserversRADIUS.inc +/etc/inc/api/api_models/APISystemConfig.inc +/etc/inc/api/api_models/APISystemAPI.inc +/etc/inc/api/api_models/APIFirewallAliasesModify.inc +/etc/inc/api/api_models/APIServicesNtpdRestart.inc +/etc/inc/api/api_models/APIServicesUnboundStart.inc +/etc/inc/api/api_models/APISystemHostnameModify.inc +/etc/inc/api/api_models/APIServicesDHCPdStart.inc +/etc/inc/api/api_models/APIUsersAddPrivs.inc +/etc/inc/api/api_models/APIServicesStart.inc +/etc/inc/api/api_models/APIFirewallNat.inc +/etc/inc/api/api_models/APIUsersDeletePrivs.inc +/etc/inc/api/api_models/APIFirewallVirtualIPsAdd.inc +/etc/inc/api/api_models/APIFirewallAliases.inc +/etc/inc/api/api_models/APISystemDNS.inc +/etc/inc/api/api_models/APIInterfaces.inc +/etc/inc/api/api_models/APIFirewallStates.inc +/etc/inc/api/api_models/APIServicesUnboundModifyHosts.inc +/etc/inc/api/api_models/APIServicesDpingerStart.inc +/etc/inc/api/api_models/APIUsersAuthserversDelete.inc +/etc/inc/api/api_models/APIUsersDeleteGroups.inc +/etc/inc/api/api_models/APISystemCertificatesAdd.inc +/etc/inc/api/api_models/APIServicesSSHdStart.inc +/etc/inc/api/api_models/APIFirewallVirtualIPsDelete.inc +/etc/inc/api/api_models/APISystemVersion.inc +/etc/inc/api/api_models/APIFirewallAliasesDelete.inc +/etc/inc/api/api_models/APIInterfacesVLANsAdd.inc +/etc/inc/api/api_models/APIFirewallStatesSizeModify.inc +/etc/inc/api/api_models/APIUsersDelete.inc +/etc/inc/api/api_models/APISystemDNSDeleteServers.inc +/etc/inc/api/api_models/APIUsersAdd.inc +/etc/inc/api/api_models/APIFirewallNatPortForwards.inc +/etc/inc/api/api_models/APIRoutingGateways.inc +/etc/inc/api/api_models/APIStatusCarp.inc +/etc/inc/api/api_models/APIUsersAuthserversLDAP.inc +/etc/inc/api/api_models/APIFirewallStatesSize.inc +/etc/inc/api/api_models/APISystemCertificatesDelete.inc +/etc/inc/api/api_models/APISystemHostname.inc +/etc/inc/api/api_models/APIServicesDpingerRestart.inc +/etc/inc/api/api_models/APIFirewallAliasesAddAddress.inc +/etc/inc/api/api_models/APIFirewallAliasesDeleteAddress.inc +/etc/inc/api/api_models/APIUsersAuthserversRADIUSDelete.inc +/etc/inc/api/api_models/APIServicesUnboundApply.inc +/etc/inc/api/api_models/APIServicesUnboundAddHosts.inc +/etc/inc/api/api_models/APIServicesNtpdStop.inc +/etc/inc/api/api_models/APIServicesDHCPdStop.inc +/etc/inc/api/api_models/APISystemAPIErrors.inc +/etc/inc/api/api_models/APIInterfacesVLANsDelete.inc +/etc/inc/api/api_models/APIFirewallRulesDelete.inc +/etc/inc/api/api_models/APIServicesSSHd.inc +/etc/inc/api/api_models/APIFirewallNatPortForwardsAdd.inc +/etc/inc/api/api_models/APIServicesDHCPdRestart.inc +/etc/inc/api/api_models/APIUsers.inc +/etc/inc/api/api_models/APIFirewallRules.inc +/etc/inc/api/api_models/APIServices.inc +/etc/inc/api/api_models/APIServicesStop.inc +/etc/inc/api/api_models/APIUsersAddGroups.inc diff --git a/tests/README.md b/tests/README.md new file mode 100644 index 000000000..c404c094a --- /dev/null +++ b/tests/README.md @@ -0,0 +1,35 @@ +Tests +===== +This directory holds our unit tests. Any script or file that aides the testing of this projects functionality should be +placed here. Ideally, each supported API call should have some sort of unit test behind it to ensure functionality does +not regress between releases. + +## Overview +Unit tests are written using Python3. These are standalone scripts that can be run from any command line with Python3 +installed. These scripts are using Python3's argparse package which includes an embedded help page by adding the `-h` +argument to your commands + +## Example Usage +```commandline +python3 tests/test_api_v1_access_token.py --url https://172.16.209.129 +OK: Response is valid +``` + +```commandline +python3 tests/test_api_v1_access_token.py -h +usage: test_api_v1_access_token.py [-h] --url URL [--username USERNAME] + [--password PASSWORD] + +Tests if pfSense APIs Access Token endpoint is operating normally + +optional arguments: + -h, --help show this help message and exit + --url URL URL of the remote pfSense host to check + --username USERNAME Username to authenticate as + --password PASSWORD Password to authenticate with + +``` + +## Output +Tests typically print an OK or ERROR status to the console depending on the tests outcome. These scripts will also +set the command's exit code to 1 upon error or 0 upon success. \ No newline at end of file diff --git a/tests/test_api_v1_access_token.py b/tests/test_api_v1_access_token.py new file mode 100644 index 000000000..cfd03d0de --- /dev/null +++ b/tests/test_api_v1_access_token.py @@ -0,0 +1,37 @@ +import requests +import argparse +import json +import sys +from urllib3.exceptions import InsecureRequestWarning + +# Variables +requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) + +# Arguments +parser = argparse.ArgumentParser(description='Tests if pfSense APIs Access Token endpoint is operating normally') +parser.add_argument('--url', dest="url", type=str, required=True,help='URL of the remote pfSense host to check') +parser.add_argument('--username', dest="username", type=str, default="admin", help='Username to authenticate as') +parser.add_argument('--password', dest="password", type=str, default="pfsense", help='Password to authenticate with') + +args = parser.parse_args() + +# Request +req_url = args.url + "/api/v1/access_token/" +req_payload = {"client-id": args.username, "client-token": args.password} +req = requests.request("POST", url=req_url, data=json.dumps(req_payload), verify=False) +if req.status_code == 200: + try: + req.json() + except json.decoder.JSONDecodeError: + print("ERROR: Expected JSON response, recieved " + str(req.content)) + sys.exit(1) + + if req.json()["return"] == 0: + print("OK: Response is valid") + sys.exit(0) + else: + print("ERROR: Expected return code of 0, received" + str(req.json()["return"])) + sys.exit(1) +else: + print("ERROR: Expected status code 200, received " + str(req.status_code)) + sys.exit(1) \ No newline at end of file diff --git a/tests/test_installed.py b/tests/test_installed.py new file mode 100644 index 000000000..bd4c50d4c --- /dev/null +++ b/tests/test_installed.py @@ -0,0 +1,28 @@ +import requests +import argparse +import json +import sys +from urllib3.exceptions import InsecureRequestWarning + +# Variables +requests.packages.urllib3.disable_warnings(category=InsecureRequestWarning) + +# Arguments +parser = argparse.ArgumentParser(description='Tests if pfSense API was successfully installed on a remote host') +parser.add_argument('--url', dest="url", type=str, required=True,help='URL of the remote pfSense host to check') +args = parser.parse_args() + +# Request +req_url = args.url + "/api/v1/system/api/errors/" +req = requests.get(req_url, verify=False) +if req.status_code == 200: + try: + req.json() + print("OK: Response is valid") + sys.exit(0) + except json.decoder.JSONDecodeError: + print("ERROR: Expected JSON response, recieved " + str(req.content)) + sys.exit(1) +else: + print("ERROR: Expected status code 200, received " + str(req.status_code)) + sys.exit(1) \ No newline at end of file diff --git a/tools/README.md b/tools/README.md new file mode 100644 index 000000000..50819da5b --- /dev/null +++ b/tools/README.md @@ -0,0 +1,26 @@ +Tools +===== +This directory includes scripts and files to aide the development of this project. Below are some basic overviews of +the scripts included. + +## MAKE_PACKAGE.PY +This script is designed to automatically generate our FreeBSD package Makefile and pkg-plist. This also attempts to +compile the package automatically if you run it on a FreeBSD system. Files are rendered using Jinja2 and templates are +found in the `templates` subdirectory + +### Usage +`python3 tools/make_package.py` + +### Dependencies +- `Jinja2` package must be installed before running (`python3 -m pip install jinja2`) + +### Output +Command will output the FreeBSD make command output. Outputs the following files: + +- `pfsense-api/pfSense-pkg-API/Makefile` : The rendered Makefile +- `pfsense-api/pfSense-pkg-API/pkg-plist`: The rendered pkg-plist +- `pfsense-api/pfSense-pkg-API/pfSense-pkg-API-.txz` : The FreeBSD package distribution file + +### Notes +- This script heavily depends on it's relative filepaths. You may execute the script from any directory, but do not move +the script to another directory. \ No newline at end of file diff --git a/tools/make_package.py b/tools/make_package.py new file mode 100644 index 000000000..1bc2ce630 --- /dev/null +++ b/tools/make_package.py @@ -0,0 +1,48 @@ +#!/usr/bin/python3 +import os +import pathlib +import jinja2 +import platform +import subprocess + +# Functions +# Custom filter for Jinja2 to determine the directory of a given file path +def dirname(path): + return os.path.dirname(path) + +# Set filepath and file variables +root_dir = pathlib.Path(__file__).absolute().parent.parent +pkg_dir = root_dir.joinpath("pfSense-pkg-API") +template_dir = root_dir.joinpath("tools").joinpath("templates") +files_dir = pkg_dir.joinpath("files") +file_paths = {"dir": [], "file": []} +excluded_files = ["pkg-deinstall.in", "pkg-install.in", "etc", "usr"] + +# Set Jijna2 environment and variables +j2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=template_dir)) +j2_env.filters["dirname"] = dirname +plist_template = j2_env.get_template("pkg-plist.j2") +makefile_template = j2_env.get_template("Makefile.j2") + +# Loop through each of our files and directories and store them for Jinja2 to render +for root, dirs, files in os.walk(files_dir, topdown=True): + root = pathlib.Path(str(root).replace(str(files_dir), "")) + for dir in dirs: + if dir not in excluded_files: + file_paths["dir"].append(root.joinpath(dir)) + for file in files: + if file not in excluded_files: + file_paths["file"].append(root.joinpath(file)) + +# Generate pkg-plist file +with open(pkg_dir.joinpath("pkg-plist"), "w") as pw: + pw.write(plist_template.render(files=file_paths)) +# Generate Makefile file +with open(pkg_dir.joinpath("Makefile"), "w") as mw: + mw.write(makefile_template.render(files=file_paths).replace(" ", "\t")) + +# If we are running on FreeBSD, make package. Otherwise display warning that package was not compiled +if platform.system() == "FreeBSD": + s = subprocess.call(["/usr/bin/make", "package", "-C", pkg_dir, "DISABLE_VULNERABILITIES=yes"]) +else: + print("WARNING: System is not FreeBSD. Generated Makefile and pkg-plist but did not attempt to make package.") diff --git a/tools/templates/Makefile.j2 b/tools/templates/Makefile.j2 new file mode 100644 index 000000000..c1107968b --- /dev/null +++ b/tools/templates/Makefile.j2 @@ -0,0 +1,28 @@ +# $FreeBSD$ + +PORTNAME=pfSense-pkg-API +PORTVERSION=0.0 +PORTREVISION=3 +CATEGORIES=sysutils +MASTER_SITES=# empty +DISTFILES=# empty +EXTRACT_ONLY=# empty +MAINTAINER= jaredhendrickson13@gmail.com +COMMENT=pfSense API package +LICENSE=APACHE20 +NO_BUILD=yes +NO_MTREE=yes +SUB_FILES=pkg-install pkg-deinstall +SUB_LIST=PORTNAME=${PORTNAME} + +do-extract: + ${MKDIR} ${WRKSRC} + +do-install: + {% for dir in files["dir"] -%} + ${MKDIR} ${STAGEDIR}{{ dir }} + {% endfor %} + {% for file in files["file"] -%} + ${INSTALL_DATA} ${FILESDIR}{{ file }} ${STAGEDIR}{{ file | dirname }} + {% endfor %} +.include \ No newline at end of file diff --git a/tools/templates/pkg-plist.j2 b/tools/templates/pkg-plist.j2 new file mode 100644 index 000000000..09c7b17c8 --- /dev/null +++ b/tools/templates/pkg-plist.j2 @@ -0,0 +1,6 @@ +{% for dir in files["dir"] -%} +@dir {{ dir }} +{% endfor %} +{% for file in files["file"] -%} +{{ file }} +{% endfor %} \ No newline at end of file From 67bf80340f093a136e55df8f4c23e5633c9e902d Mon Sep 17 00:00:00 2001 From: Jared Hendrickson Date: Wed, 26 Aug 2020 14:56:24 -0600 Subject: [PATCH 16/16] Final touch ups, ready to merge --- tools/README.md | 4 +++- tools/make_package.py | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tools/README.md b/tools/README.md index 50819da5b..510725d70 100644 --- a/tools/README.md +++ b/tools/README.md @@ -19,7 +19,9 @@ Command will output the FreeBSD make command output. Outputs the following files - `pfsense-api/pfSense-pkg-API/Makefile` : The rendered Makefile - `pfsense-api/pfSense-pkg-API/pkg-plist`: The rendered pkg-plist -- `pfsense-api/pfSense-pkg-API/pfSense-pkg-API-.txz` : The FreeBSD package distribution file +- `pfsense-api/pfSense-pkg-API/pfSense-pkg-API-.txz` : The FreeBSD package distribution file. On FreeBSD 11, +this should be located in the `pfsense-api/pfSense-pkg-API` directory after completion. On FreeBSD 12 it should be +located in the `pfsense-api/pfSense-pkg-API/work/pkg` directory. ### Notes - This script heavily depends on it's relative filepaths. You may execute the script from any directory, but do not move diff --git a/tools/make_package.py b/tools/make_package.py index 1bc2ce630..fdd32e563 100644 --- a/tools/make_package.py +++ b/tools/make_package.py @@ -19,7 +19,7 @@ def dirname(path): excluded_files = ["pkg-deinstall.in", "pkg-install.in", "etc", "usr"] # Set Jijna2 environment and variables -j2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=template_dir)) +j2_env = jinja2.Environment(loader=jinja2.FileSystemLoader(searchpath=str(template_dir))) j2_env.filters["dirname"] = dirname plist_template = j2_env.get_template("pkg-plist.j2") makefile_template = j2_env.get_template("Makefile.j2") @@ -29,10 +29,10 @@ def dirname(path): root = pathlib.Path(str(root).replace(str(files_dir), "")) for dir in dirs: if dir not in excluded_files: - file_paths["dir"].append(root.joinpath(dir)) + file_paths["dir"].append(str(root.joinpath(dir))) for file in files: if file not in excluded_files: - file_paths["file"].append(root.joinpath(file)) + file_paths["file"].append(str(root.joinpath(file))) # Generate pkg-plist file with open(pkg_dir.joinpath("pkg-plist"), "w") as pw: