diff --git a/README.md b/README.md index 8e9aed226..9595fa6a4 100644 --- a/README.md +++ b/README.md @@ -63,4 +63,6 @@ If you would like to [contribute](https://github.com/newrelic/newrelic-php-agent To all [contributors](https://github.com/newrelic/newrelic-php-agent/graphs/contributors), we thank you! Without your contribution, this project would not be what it is today. We also host a community project page dedicated to the [New Relic PHP agent](https://opensource.newrelic.com/projects/newrelic/newrelic-php-agent). ## License -The PHP agent is licensed under the [Apache 2.0](https://apache.org/licenses/LICENSE-2.0.txt) License and also uses source code from third-party libraries. You can find full details on which libraries are used and the terms under which they are licensed in the third-party notices document. +Except as described below, the New Relic PHP agent is licensed under the [Apache 2.0](https://apache.org/licenses/LICENSE-2.0.txt) License and also uses source code from third-party libraries. You can find full details on which libraries are used and the terms under which they are licensed in the third-party notices document. + +The New Relic PHP Security agent [csec-php-agent](https://github.com/newrelic/csec-php-agent) is licensed under the New Relic Pre-Release Software Notice. The New Relic PHP Security agent module may be integrated like the New Relic PHP agent. diff --git a/THIRD_PARTY_NOTICES.md b/THIRD_PARTY_NOTICES.md index 7067fda8a..96d31f7c3 100644 --- a/THIRD_PARTY_NOTICES.md +++ b/THIRD_PARTY_NOTICES.md @@ -25,6 +25,7 @@ by e-mailing [open-source@newrelic.com](mailto:open-source@newrelic.com). * [Portable OpenSSH](#portable-openssh) * [TSRM](#tsrm) * [Symfony](#symfony) +* [New Relic PHP Security Agent](#new-relic-php-security-agent) ## cJSON @@ -957,3 +958,67 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ``` + +## New Relic PHP Security Agent + +This product includes source derived from +[New Relic csec-php-agent](https://github.com/newrelic/csec-php-agent), distributed under the +[New Relic Pre-Release Software Notice](https://github.com/newrelic/csec-php-agent/blob/main/LICENSE): + +## New Relic Pre-Release Software Notice + +### Acceptance + +``` +This software license applies to the repositories, directories, or files that it has been associated with (referred to here as the "Pre-Release Software"). In order to get any license under these terms, you must agree to them as both strict obligations and conditions to all your licenses. +License + +New Relic, Inc. ("New Relic") grants you a license under its copyrights and patents to do everything necessary for you to use the Pre-Release Software for the Permitted Purposes and subject to your compliance with the Confidentiality terms and all other Restrictions on Use. All other uses are prohibited. +``` + +### Permitted Purposes + +``` +The Pre-Release Software is solely for use with New Relic's proprietary SaaS service ("New Relic Service"). To use the Pre-Release Software you must have a valid and active subscription agreement to the New Relic Service. You may not use the Pre-Release Software in conjunction with any other similar service. + +Use of the Pre-Release Software is limited to evaluation and feedback, including pull requests, of the pre-release features and capabilities. The use of the Pre-Release Software requires a signed New Relic Pre-Release Agreement. The New Relic Pre-Release Agreement will control in event of a conflict with this license. The Pre-Release Software is also subject to the New Relic pre-release policy. A copy of the New Relic Pre-release policy available at https://docs.newrelic.com/docs/licenses/license-information/referenced-policies/new-relic-pre-release-policy/. +``` + +### Confidentiality + +``` +Any Pre-Release Software not posted publicly contains New Relic Trade Secrets. You may not disclose the features and capabilities of the Pre-Release Software to any third party by any means. You will use commercially reasonable measures to maintain the secrecy of the features and capabilities of the Pre-Release Software. Without limiting the permissions you receive under any open source software ("OSS") license, you may not disclose the source code of any OSS included as part of the Pre-Release Software in a way that discloses the New Relic Trade Secrets. +``` + +### Restrictions on Use + +``` +1. The use of the Pre-Release Software is limited to you and your organization. You cannot expose the Pre-Release Software to third parties via the network or use the Pre-Release Software on behalf of anyone else. +2. You cannot modify the Pre-Release Software. You can compile or link the Pre-Release Software into your programs as described in the New Relic Documentation, but you cannot otherwise make any derivative works of the Pre-Release Software. +3. You cannot reverse engineer or decompile the Pre-Release Software. +4. You cannot use the Pre-Release Software for competitive analysis, benchmarking, or any purposes that compete with or are intended to compete with New Relic. +5. You cannot remove or obscure any proprietary notices in the Pre-Release Software. +6. You can only copy the Software to the extent needed to use the Pre-Release Software within your organization. You cannot distribute the Software to any third parties. +7. You can only use the New Relic Services and the Pre-Release Software consistent with New Relic's product Documentation and Acceptable Use Policy. +``` + +### Intellectual Property + +``` +Any feedback provided to New Relic about the Pre-Release Software, including feedback provided as source code, comments, or other copyrightable or patentable material, is provided to New Relic under the terms of the Apache Software License, version 2. If you do not provide attribution information or a copy of the license with your feedback, you waive the performance of those requirements of the Apache License with respect to New Relic. The license grant regarding any feedback is irrevocable and persists past the termination of this license. +``` + +### Termination + +``` +This license terminates when the Pre-Release Software stops being provided by New Relic or when described by the New Relic Pre-Release Agreement, whichever comes first. When this license terminates, you have a commercially reasonable time to either remove the Pre-Release Software from your systems or to upgrade to a publicly released version of the Software. +``` + +### Other Terms + +``` +1. The Pre-Release Software is provided "AS IS." New Relic disclaims all warranties, whether express, implied, statutory or otherwise, including warranties of merchantability, fitness for a particular purpose, title and noninfringement. +2. The Pre-Release Software has known flaws. You use the Pre-Release Software at your own risk. +3. To the full extent permitted by law, New Relic will have no liability arising from or related to the Pre-Release Software or from this license for any direct, indirect, special, incidental, or consequential damages of any kind, even if advised of their possibility in advance, and regardless of legal theory (whether contract, tort, negligence, strict liability or otherwise). +4. The Pre-Release Software may contain third-party software, including open source software. The third party software is governed by its own license and subject to its own terms, disclosed in the provided licensing information associated with the third party components. If a source code disclosure is required under the terms of the license covering any OSS component, the source code can be requested of New Relic by emailing opensource@newrelic.com. +``` \ No newline at end of file diff --git a/agent/config.m4 b/agent/config.m4 index 8d9e42b24..8beef6a4f 100644 --- a/agent/config.m4 +++ b/agent/config.m4 @@ -220,7 +220,7 @@ if test "$PHP_NEWRELIC" = "yes"; then php_pdo_mysql.c php_pdo_pgsql.c php_pgsql.c php_psr7.c php_redis.c \ php_rinit.c php_rshutdown.c php_samplers.c php_stack.c \ php_stacked_segment.c php_txn.c php_user_instrument.c \ - php_user_instrument_hashmap.c php_vm.c php_wrapper.c" + php_user_instrument_hashmap.c php_vm.c php_wrapper.c csec_metadata.c" FRAMEWORKS="fw_cakephp.c fw_codeigniter.c fw_drupal8.c \ fw_drupal.c fw_drupal_common.c fw_joomla.c fw_kohana.c \ fw_laminas3.c fw_laravel.c fw_laravel_queue.c fw_lumen.c \ diff --git a/agent/csec_metadata.c b/agent/csec_metadata.c new file mode 100644 index 000000000..ba43b9c1a --- /dev/null +++ b/agent/csec_metadata.c @@ -0,0 +1,48 @@ +#include "csec_metadata.h" +#include "util_strings.h" +#include "php_hash.h" +#include "php_api_internal.h" + +static void nr_csec_php_add_assoc_string_const(zval* arr, + const char* key, + const char* value) { + char* val = NULL; + + if (NULL == arr || NULL == key || NULL == value) { + return; + } + + val = nr_strdup(value); + nr_php_add_assoc_string(arr, key, val); + nr_free(val); +} + +#ifdef TAGS +void zif_newrelic_get_security_metadata(void); /* ctags landing pad only */ +void newrelic_get_security_metadata(void); /* ctags landing pad only */ +#endif +PHP_FUNCTION(newrelic_get_security_metadata) { + + NR_UNUSED_RETURN_VALUE; + NR_UNUSED_RETURN_VALUE_PTR; + NR_UNUSED_RETURN_VALUE_USED; + NR_UNUSED_THIS_PTR; + NR_UNUSED_EXECUTE_DATA; + + array_init(return_value); + + nr_csec_php_add_assoc_string_const(return_value, KEY_ENTITY_NAME, nr_app_get_entity_name(NRPRG(app))); + nr_csec_php_add_assoc_string_const(return_value, KEY_ENTITY_TYPE, nr_app_get_entity_type(NRPRG(app))); + nr_csec_php_add_assoc_string_const(return_value, KEY_ENTITY_GUID, nr_app_get_entity_guid(NRPRG(app))); + nr_csec_php_add_assoc_string_const(return_value, KEY_HOSTNAME, nr_app_get_host_name(NRPRG(app))); + nr_csec_php_add_assoc_string_const(return_value, KEY_LICENSE, NRPRG(license).value); + + if (NRPRG(app)) { + nr_csec_php_add_assoc_string_const(return_value, KEY_AGENT_RUN_ID, NRPRG(app)->agent_run_id); + nr_csec_php_add_assoc_string_const(return_value, KEY_ACCOUNT_ID, NRPRG(app)->account_id); + nr_csec_php_add_assoc_string_const(return_value, KEY_PLICENSE, NRPRG(app)->plicense); + int high_security = NRPRG(app)->info.high_security; + add_assoc_long(return_value, KEY_HIGH_SECURITY, (long)high_security); + } + +} diff --git a/agent/csec_metadata.h b/agent/csec_metadata.h new file mode 100644 index 000000000..1716f6da4 --- /dev/null +++ b/agent/csec_metadata.h @@ -0,0 +1,12 @@ +#include "php_agent.h" +#include "util_hashmap.h" + +#define KEY_ENTITY_NAME "entity.name" +#define KEY_ENTITY_TYPE "entity.type" +#define KEY_ENTITY_GUID "entity.guid" +#define KEY_HOSTNAME "hostname" +#define KEY_AGENT_RUN_ID "agent.run.id" +#define KEY_ACCOUNT_ID "account.id" +#define KEY_LICENSE "license" +#define KEY_PLICENSE "plicense" +#define KEY_HIGH_SECURITY "high_security" diff --git a/agent/php_api_internal.h b/agent/php_api_internal.h index 61db36648..f8ae9a653 100644 --- a/agent/php_api_internal.h +++ b/agent/php_api_internal.h @@ -16,6 +16,8 @@ */ extern PHP_FUNCTION(newrelic_get_request_metadata); +extern PHP_FUNCTION(newrelic_get_security_metadata); + #ifdef ENABLE_TESTING_API /* diff --git a/agent/php_globals.h b/agent/php_globals.h index c73b93e43..fb2e35558 100644 --- a/agent/php_globals.h +++ b/agent/php_globals.h @@ -66,6 +66,8 @@ typedef struct _nrphpglobals_t { int instrument_internal; /* newrelic.transaction_tracer.internal_functions_enabled */ int high_security; /* newrelic.high_security */ + bool nr_security_agent_enabled; /* newrelic.security.agent.enabled */ + bool nr_security_enabled; /* newrelic.security.enabled */ int apache_major; /* Apache major version */ int apache_minor; /* Apache minor version */ diff --git a/agent/php_minit.c b/agent/php_minit.c index 7cf4f3e8e..6a013e238 100644 --- a/agent/php_minit.c +++ b/agent/php_minit.c @@ -719,6 +719,13 @@ PHP_MINIT_FUNCTION(newrelic) { nr_wordpress_minit(); nr_php_set_opcode_handlers(); + if (!NR_PHP_PROCESS_GLOBALS(nr_security_agent_enabled) || !NR_PHP_PROCESS_GLOBALS(nr_security_enabled) || NR_PHP_PROCESS_GLOBALS(high_security)) { + nrl_info(NRL_INIT, "New Relic Security is completely disabled by one of the user provided config `newrelic.security.enabled`, `newrelic.security.agent.enabled` or `newrelic.high_security`. Not loading security capabilities."); + nrl_debug(NRL_INIT, "newrelic.security.agent.enabled : %s", NR_PHP_PROCESS_GLOBALS(nr_security_enabled) ? "true" : "false"); + nrl_debug(NRL_INIT, "newrelic.security.enabled : %s", NR_PHP_PROCESS_GLOBALS(nr_security_agent_enabled) ? "true" : "false"); + nrl_debug(NRL_INIT, "newrelic.high_security : %s", NR_PHP_PROCESS_GLOBALS(high_security) ? "true" : "false"); + } + nrl_debug(NRL_INIT, "MINIT processing done"); #if ZEND_MODULE_API_NO >= ZEND_8_0_X_API_NO /* PHP 7.4+ */ NR_PHP_PROCESS_GLOBALS(zend_offset) = zend_get_resource_handle(dummy); diff --git a/agent/php_newrelic.c b/agent/php_newrelic.c index 210c04428..80d4466a1 100644 --- a/agent/php_newrelic.c +++ b/agent/php_newrelic.c @@ -343,9 +343,11 @@ static zend_function_entry newrelic_functions[] = { #ifdef PHP8 PHP_FE(newrelic_get_linking_metadata, newrelic_arginfo_void) PHP_FE(newrelic_get_trace_metadata, newrelic_arginfo_void) + PHP_FE(newrelic_get_security_metadata, newrelic_arginfo_void) #else PHP_FE(newrelic_get_linking_metadata, 0) PHP_FE(newrelic_get_trace_metadata, 0) + PHP_FE(newrelic_get_security_metadata, 0) #endif /* PHP 8 */ /* * Integration test helpers diff --git a/agent/php_nrini.c b/agent/php_nrini.c index 2ed62eb10..3029392ad 100644 --- a/agent/php_nrini.c +++ b/agent/php_nrini.c @@ -515,6 +515,58 @@ static PHP_INI_MH(nr_high_security_mh) { return SUCCESS; } +static PHP_INI_MH(nr_security_enabled_mh) { + int val; + + (void)entry; + (void)NEW_VALUE_LEN; + (void)mh_arg1; + (void)mh_arg2; + (void)mh_arg3; + (void)stage; + NR_UNUSED_TSRMLS; + + val = nr_bool_from_str(NEW_VALUE); + + if (-1 == val) { + return FAILURE; + } + + if (val) { + NR_PHP_PROCESS_GLOBALS(nr_security_enabled) = true; + } else { + NR_PHP_PROCESS_GLOBALS(nr_security_enabled) = false; + } + + return SUCCESS; +} + +static PHP_INI_MH(nr_security_agent_enabled_mh) { + int val; + + (void)entry; + (void)NEW_VALUE_LEN; + (void)mh_arg1; + (void)mh_arg2; + (void)mh_arg3; + (void)stage; + NR_UNUSED_TSRMLS; + + val = nr_bool_from_str(NEW_VALUE); + + if (-1 == val) { + return FAILURE; + } + + if (val) { + NR_PHP_PROCESS_GLOBALS(nr_security_agent_enabled) = true; + } else { + NR_PHP_PROCESS_GLOBALS(nr_security_agent_enabled) = false; + } + + return SUCCESS; +} + static PHP_INI_MH(nr_preload_framework_library_detection_mh) { int val; @@ -2009,6 +2061,18 @@ PHP_INI_ENTRY_EX("newrelic.high_security", nr_high_security_mh, 0) +PHP_INI_ENTRY_EX("newrelic.security.agent.enabled", + "0", + NR_PHP_SYSTEM, + nr_security_agent_enabled_mh, + 0) + +PHP_INI_ENTRY_EX("newrelic.security.enabled", + "0", + NR_PHP_SYSTEM, + nr_security_enabled_mh, + 0) + /* * Feature flag handling. */ diff --git a/agent/scripts/newrelic.ini.template b/agent/scripts/newrelic.ini.template index 0e5da6ab3..6526f6a7c 100644 --- a/agent/scripts/newrelic.ini.template +++ b/agent/scripts/newrelic.ini.template @@ -13,6 +13,7 @@ ; If you do not use an absolute path then the file must be installed in the ; active configuration's extension directory. extension = "newrelic.so" +extension = "newrelic_security.so" [newrelic] ; Setting: newrelic.enabled @@ -1332,3 +1333,86 @@ newrelic.daemon.logfile = "/var/log/newrelic/newrelic-daemon.log" ; for vulnerability management. ; ;newrelic.vulnerability_management.package_detection.enabled = true + +; Setting: newrelic.security.enabled +; Type : boolean +; Scope : system +; Default: false +; Info : Indicates if attack detection security module is to be enabled +; +;newrelic.security.enabled = true + +; Setting: newrelic.security.mode +; Type : string +; Scope : system +; Default: "IAST" +; Info : Security module provides two modes "IAST" or "RASP" +; See documentation for more details +; +;newrelic.security.mode = "IAST" + +; Setting: newrelic.security.validator_service_endpoint_url +; Type : string +; Scope : system +; Default: "wss://csec.nr-data.net" +; Info : New Relic<80><99>s security module SaaS connection URLs +; +;newrelic.security.validator_service_url = "wss://csec.nr-data.net" + +; Setting: newrelic.security.agent.enabled +; Type : boolean +; Scope : system +; Default: false +; Info : Used to enable security module, default false is equivalent to +; security module not even loaded. If this setting is set to true, +; then only security module is loaded and to enable it, a restart +; of application is required. This is different than +; newrelic.security.enabled, in terms that security.enabled decides +; runtime behavior of security module but security.agent.enabled +; would not even load the security module when set to false +; +;newrelic.security.agent.enabled = false + +; Setting: newrelic.security.detection.rci.enabled +; Type : boolean +; Scope : system +; Default: true +; Info : Indicates if detection of remote code injection attack category is to be enabled +; +;newrelic.security.detection.rci.enabled = true + +; Setting: newrelic.security.detection.rxss.enabled +; Type : boolean +; Scope : system +; Default: true +; Info : Indicates if detection of reflected xss attack category is to be enabled +; +;newrelic.security.detection.rxss.enabled = true + +; Setting: newrelic.security.detection.deserialization.enabled +; Type : boolean +; Scope : system +; Default: true +; Info : Indicates if detection of deserialization attack category is to be enabled +; +;newrelic.security.detection.deserialization.enabled = true + +; Setting: newrelic.security.request.body_limit +; Type : unsigned integer +; Scope : system +; Default: 300 +; Info : Sets the maximum limit of the request body to be read in kb. +; +;newrelic.security.request.body_limit = 300 + +; Setting: newrelic.security.validator.client_ssl_cert_filepath +; Type : string +; Scope : system +; Default: none +; Info : Sets the full path of the client certificate in PEM +; format. When set, this certificate will be used to +; authenticate the New Relic IAST Security Engine's url. If +; not set, the default certificate will be used. Make +; sure the file permissions are correct. +; +;newrelic.security.validator.client_ssl_cert_filepath = "" diff --git a/axiom/cmd_appinfo_transmit.c b/axiom/cmd_appinfo_transmit.c index 806a66f63..b33c9c250 100644 --- a/axiom/cmd_appinfo_transmit.c +++ b/axiom/cmd_appinfo_transmit.c @@ -287,6 +287,7 @@ nr_status_t nr_cmd_appinfo_process_reply(const uint8_t* data, int reply_len; const char* reply_json; const char* entity_guid; + const char* account_id; /* Csec : Added for extracting account_id */ if ((NULL == data) || (0 == len)) { return NR_FAILURE; @@ -384,6 +385,20 @@ nr_status_t nr_cmd_appinfo_process_reply(const uint8_t* data, app->entity_guid = NULL; } + /* + * Csec : Added for extracting account_id + */ + nr_free(app->account_id); + account_id = nro_get_hash_string(app->connect_reply, "account_id", NULL); + if (NULL != account_id) { + app->account_id = nr_strdup(account_id); + } else { + app->account_id = NULL; + } + /* + * Csec : Added for extracting account_id + */ + nrl_debug(NRL_ACCT, "APPINFO reply full app='%.*s' agent_run_id=%s", NRP_APPNAME(app->info.appname), app->agent_run_id); diff --git a/axiom/nr_app.c b/axiom/nr_app.c index d20a56f65..cbfd2c97c 100644 --- a/axiom/nr_app.c +++ b/axiom/nr_app.c @@ -121,6 +121,7 @@ void nr_app_destroy(nrapp_t** app_ptr) { nr_free(app->host_name); nr_free(app->entity_guid); nr_free(app->entity_name); + nr_free(app->account_id); nr_rules_destroy(&app->url_rules); nr_rules_destroy(&app->txn_rules); nr_segment_terms_destroy(&app->segment_terms); diff --git a/axiom/nr_app.h b/axiom/nr_app.h index d05a7f364..c33bb54c7 100644 --- a/axiom/nr_app.h +++ b/axiom/nr_app.h @@ -105,6 +105,7 @@ typedef struct _nrapp_t { char* host_name; /* Local host name reported to the daemon */ char* entity_name; /* Entity name related to this application */ char* entity_guid; /* Entity guid related to this application */ + char* account_id; /* Security : Added for getting account id */ time_t last_daemon_query; /* Used by agent: Last time we queried daemon about this app */ int failed_daemon_query_count; /* Used by agent: Number of times daemon query