(UTC) (envelope-from git@FreeBSD.org) Received: from mxrelay.nyi.freebsd.org (mxrelay.nyi.freebsd.org [IPv6:2610:1c1:1:606c::19:3]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (4096 bits) server-digest SHA256 client-signature RSA-PSS (4096 bits) client-digest SHA256) (Client CN "mxrelay.nyi.freebsd.org", Issuer "R13" (verified OK)) by mx1.freebsd.org (Postfix) with ESMTPS id 4ds6KK2lqHz3h90 for ; Thu, 15 Jan 2026 02:25:41 +0000 (UTC) (envelope-from git@FreeBSD.org) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1768443941; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=YCVI7tk1n/DyLBNQdEQBRZly4q5GyAEp3P4oYpM+Puo=; b=wBBPAPCoykKW7R0oJXddPnWqHRoAIzd2+Rs4M6odE4KPPU87ml0v2Br6Hny64IbEI9QtAl GeXm/7x6siYqcBcRkvatNPawVMSbQuNDvu1764UjKfZOwIb94n1C2Xc0FuDjLjZ0QiZHO5 9zC6Y02YIIMn2/j77INXY3kj3rg/jShZQYIzqzITNcjczi2yi3JeEXjGrroL4+Ke9p7jvH SxIB5H3zbuaTat4ZgUe7lyPLnei1D3vjUTEjj88eRPTIEJGPHePBeU1wHc3TBBKFBlx3Ha 99rQ9GY2U/fCw5UDDK52QV1Kc0vvQKBgqCtpGdprm9LiX2UUjbHIzCiVjpLs2g== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=freebsd.org; s=dkim; t=1768443941; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding; bh=YCVI7tk1n/DyLBNQdEQBRZly4q5GyAEp3P4oYpM+Puo=; b=LbtnFOLpamy39NLD5ldjH9qdj9/2v1HmBWTnDY++DeJ2ujeFqNs/Miu6IVEdSFkBaDnfHR 7NHiQ4Lt+DUKn65BMJ0uBJ41bu7x74NyClOvGwer1yB+q0yjlEQd5Vc04Crz0KZKoLvNpn 2R0rJM1uVWCTs6QVTf1IuYUc6a3IP2fre0UGYK/yBII3dIPPCCUFwaLoDGtr/o94A+owKT rY0rCRuWun59Jn5gnBuIrPYnkhrCRJtyVg1a58eoj5ckmf6+s+iU1Fr1D8lldXvv1HACqy WXQQvxBaadorxcQZ/B3yyIpHwfh1P5H291OwCNuEvGg1hel+PH2k+lw0Rm3ehw== ARC-Seal: i=1; s=dkim; d=freebsd.org; t=1768443941; a=rsa-sha256; cv=none; b=ZQloB/D3aV0XUw7k3V8RHeFYJuT/N3Op8sIiTJ8tLgEqZY8MLUYQpXYX3A+EKOcfRvgd6q uKMhIk+o0CKtGKfgnhQIM+MDZdRX1Be4zrziiCLYFopcCKhzrfLwL7olT/QJAFy/ERqGe1 l5lR9SO8kPqcYLAfU0UDGQF0M8l41yvWB4Wy/+3epOk2/vhZ+3SK7l0ZYde5/r8+zLW6CW w2emR7HadyInxaHq2NhBtto+GtO12hU53/KSjTtSIFfsUwEk16vo224w9OCJln9EULnleK MW0TAM5aQKoT2oZTo6QMDRdaFQ2tZFyuJbPsJvbFpr/2hJgGHIs0sbvTrk1Z7A== ARC-Authentication-Results: i=1; mx1.freebsd.org; none Received: from gitrepo.freebsd.org (gitrepo.freebsd.org [IPv6:2610:1c1:1:6068::e6a:5]) by mxrelay.nyi.freebsd.org (Postfix) with ESMTP id 4ds6KK0XSpz1NJ4 for ; Thu, 15 Jan 2026 02:25:41 +0000 (UTC) (envelope-from git@FreeBSD.org) Received: from git (uid 1279) (envelope-from git@FreeBSD.org) id 928c by gitrepo.freebsd.org (DragonFly Mail Agent v0.13+ on gitrepo.freebsd.org); Thu, 15 Jan 2026 02:25:41 +0000 To: ports-committers@FreeBSD.org, dev-commits-ports-all@FreeBSD.org, dev-commits-ports-main@FreeBSD.org From: Jose Alonso Cardenas Marquez Subject: git: 88938fcfb8d1 - main - security/wazuh-agent: Add users and groups function support List-Id: Commit messages for all branches of the ports repository List-Archive: https://lists.freebsd.org/archives/dev-commits-ports-all List-Help: List-Post: List-Subscribe: List-Unsubscribe: X-BeenThere: dev-commits-ports-all@freebsd.org Sender: owner-dev-commits-ports-all@FreeBSD.org MIME-Version: 1.0 Content-Type: text/plain; charset=utf-8 Content-Transfer-Encoding: 8bit X-Git-Committer: acm X-Git-Repository: ports X-Git-Refname: refs/heads/main X-Git-Reftype: branch X-Git-Commit: 88938fcfb8d169ecc932f3bea0d51c5316e43372 Auto-Submitted: auto-generated Date: Thu, 15 Jan 2026 02:25:41 +0000 Message-Id: <69685025.928c.55c55646@gitrepo.freebsd.org> The branch main has been updated by acm: URL: https://cgit.FreeBSD.org/ports/commit/?id=88938fcfb8d169ecc932f3bea0d51c5316e43372 commit 88938fcfb8d169ecc932f3bea0d51c5316e43372 Author: Jose Alonso Cardenas Marquez AuthorDate: 2026-01-15 02:18:58 +0000 Commit: Jose Alonso Cardenas Marquez CommitDate: 2026-01-15 02:18:58 +0000 security/wazuh-agent: Add users and groups function support - Now wazuh-agent can obtain users and groups information - Fix start_time data to show correct datetime data from wazuh-dashboard processes option - Bump PORTREVISION --- security/wazuh-agent/Makefile | 3 +- .../files/patch-src_data__provider-CMakeLists.txt | 39 +++ .../files/patch-src_data__provider_CMakeLists.txt | 11 - ...atch-src_data__provider_src-sysInfoFreeBSD.cpp} | 300 +++++++++++++++++---- ...__provider_src_extended__sources-CMakeLists.txt | 18 ++ ...der_src_extended__sources_groups-CMakeLists.txt | 22 ++ ...nded__sources_groups_include-groups_freebsd.hpp | 46 ++++ ..._sources_groups_include-user_groups_freebsd.hpp | 84 ++++++ ...extended__sources_groups_src-groups_freebsd.hpp | 95 +++++++ ...ded__sources_groups_src-user_groups_freebsd.hpp | 262 ++++++++++++++++++ ...ider_src_extended__sources_users-CMakeLists.txt | 21 ++ ...urces_users_include-logged_in_users_freebsd.hpp | 37 +++ ...tended__sources_users_include-users_freebsd.hpp | 69 +++++ ...__sources_users_src-logged_in_users_freebsd.cpp | 72 +++++ ...c_extended__sources_users_src-users_freebsd.cpp | 106 ++++++++ ...ended__sources_wrappers_unix-iutmpx_wrapper.hpp | 12 + ...tended__sources_wrappers_unix-utmpx_wrapper.hpp | 16 ++ ...sources_wrappers_unix_freebsd-group_wrapper.hpp | 93 +++++++ ...ources_wrappers_unix_freebsd-igroup_wrapper.hpp | 70 +++++ ...urces_wrappers_unix_freebsd-ipasswd_wrapper.hpp | 72 +++++ ...ources_wrappers_unix_freebsd-passwd_wrapper.hpp | 96 +++++++ 21 files changed, 1482 insertions(+), 62 deletions(-) diff --git a/security/wazuh-agent/Makefile b/security/wazuh-agent/Makefile index 7fce39e00bf9..c5f31572243c 100644 --- a/security/wazuh-agent/Makefile +++ b/security/wazuh-agent/Makefile @@ -1,7 +1,7 @@ PORTNAME= wazuh DISTVERSION= 4.14.1 DISTVERSIONPREFIX= v -PORTREVISION= 5 +PORTREVISION= 6 CATEGORIES= security MASTER_SITES= https://packages.wazuh.com/deps/47/libraries/sources/:wazuh_sources PKGNAMESUFFIX= -agent @@ -120,6 +120,7 @@ post-extract: @cd ${WRKSRC}/src/external && ${EXTRACT_CMD} ${EXTRACT_BEFORE_ARGS} ${_DISTDIR}/${FILE:S/:wazuh_sources//} ${EXTRACT_AFTER_ARGS} .endfor @${MKDIR} ${WRKSRC}/ruleset/sca/freebsd + @${MKDIR} ${WRKSRC}/src/data_provider/src/extended_sources/wrappers/unix/freebsd @cd ${WRKDIR} && ${EXTRACT_CMD} ${EXTRACT_BEFORE_ARGS} ${_DISTDIR}/${WAZUH_EXTRAFILE} ${EXTRACT_AFTER_ARGS} @${MV} ${WRKDIR}/${PORTNAME}-freebsd-${WAZUH_EXTRAFILE_TAGNAME} ${WRKDIR}/wazuh-freebsd @cd ${WRKDIR}/wazuh-freebsd/var/ossec/ruleset/sca && ${CP} *.yml ${WRKSRC}/ruleset/sca/freebsd/ diff --git a/security/wazuh-agent/files/patch-src_data__provider-CMakeLists.txt b/security/wazuh-agent/files/patch-src_data__provider-CMakeLists.txt new file mode 100644 index 000000000000..03dd0efcd223 --- /dev/null +++ b/security/wazuh-agent/files/patch-src_data__provider-CMakeLists.txt @@ -0,0 +1,39 @@ +--- src/data_provider/CMakeLists.txt 2025-11-07 08:46:03.000000000 +0000 ++++ src/data_provider/CMakeLists.txt 2026-01-13 15:21:46.999172000 +0000 +@@ -104,7 +104,6 @@ + include_directories(${CMAKE_SOURCE_DIR}/src/extended_sources/wrappers/unix/darwin) + endif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + +- + if(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") + link_directories(${INSTALL_PREFIX}/lib) + endif(CMAKE_SYSTEM_NAME STREQUAL "HP-UX") +@@ -119,6 +118,11 @@ + include_directories(${CMAKE_SOURCE_DIR}/src/extended_sources/wrappers/unix/darwin) + endif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + ++if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") ++ include_directories(${CMAKE_SOURCE_DIR}/src/extended_sources/wrappers/unix/) ++ include_directories(${CMAKE_SOURCE_DIR}/src/extended_sources/wrappers/unix/freebsd) ++endif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") ++ + link_directories(${SRC_FOLDER}) + link_directories(${SRC_FOLDER}/external/sqlite/) + link_directories(${SRC_FOLDER}/external/cJSON/) +@@ -210,6 +214,7 @@ + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux" OR + CMAKE_SYSTEM_NAME STREQUAL "Darwin" OR ++ CMAKE_SYSTEM_NAME STREQUAL "FreeBSD" OR + CMAKE_SYSTEM_NAME STREQUAL "Windows") + add_subdirectory(src/extended_sources) + endif() +@@ -250,7 +255,7 @@ + target_link_libraries(sysinfo cjson ${SRC_FOLDER}/external/libplist/bin/lib/libplist-2.0.a ${iokit_lib} ${corefoundation_lib} groups users services browser_extensions) + endif(CMAKE_SYSTEM_NAME STREQUAL "Windows") + +-target_link_libraries(sysinfo wazuhext) ++target_link_libraries(sysinfo nghttp2 wazuhext users groups) + + if(CMAKE_SYSTEM_NAME STREQUAL "Linux") + set(CURL_DEP "wazuhext") diff --git a/security/wazuh-agent/files/patch-src_data__provider_CMakeLists.txt b/security/wazuh-agent/files/patch-src_data__provider_CMakeLists.txt deleted file mode 100644 index 0cec8b79cb08..000000000000 --- a/security/wazuh-agent/files/patch-src_data__provider_CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ ---- src/data_provider/CMakeLists.txt.orig 2023-05-24 19:23:05 UTC -+++ src/data_provider/CMakeLists.txt -@@ -152,7 +152,7 @@ elseif(APPLE) - target_link_libraries(sysinfo cjson ${SRC_FOLDER}/external/libplist/bin/lib/libplist-2.0.a) - endif(CMAKE_SYSTEM_NAME STREQUAL "Windows") - --target_link_libraries(sysinfo wazuhext) -+target_link_libraries(sysinfo nghttp2 wazuhext) - - - if(APPLE) diff --git a/security/wazuh-agent/files/patch-src-data_provider-src_sysInfoFreeBSD.cpp b/security/wazuh-agent/files/patch-src_data__provider_src-sysInfoFreeBSD.cpp similarity index 68% rename from security/wazuh-agent/files/patch-src-data_provider-src_sysInfoFreeBSD.cpp rename to security/wazuh-agent/files/patch-src_data__provider_src-sysInfoFreeBSD.cpp index 9fb64aa3c105..8ef1dd376f3e 100644 --- a/security/wazuh-agent/files/patch-src-data_provider-src_sysInfoFreeBSD.cpp +++ b/security/wazuh-agent/files/patch-src_data__provider_src-sysInfoFreeBSD.cpp @@ -1,6 +1,6 @@ ---- src/data_provider/src/sysInfoFreeBSD.cpp.orig 2025-11-07 04:46:03.000000000 -0400 -+++ src/data_provider/src/sysInfoFreeBSD.cpp 2026-01-06 19:37:15.309352000 -0400 -@@ -11,20 +11,28 @@ +--- src/data_provider/src/sysInfoFreeBSD.cpp 2025-11-07 08:46:03.000000000 +0000 ++++ src/data_provider/src/sysInfoFreeBSD.cpp 2026-01-14 16:59:37.014537000 +0000 +@@ -11,20 +11,33 @@ #include "sysInfo.hpp" #include "cmdHelper.h" #include "stringHelper.h" @@ -13,6 +13,11 @@ #include #include "sharedDefs.h" +#include ++#include "groups_freebsd.hpp" ++#include "user_groups_freebsd.hpp" ++#include "logged_in_users_freebsd.hpp" ++#include "sudoers_unix.hpp" ++#include "users_freebsd.hpp" +const std::string PKG_DB_PATHNAME {"/var/db/pkg/local.sqlite"}; +const std::string PKG_QUERY {"SELECT p.name, p.maintainer, p.version, p.arch, p.comment, p.flatsize, p.time, v.annotation AS repository,p.origin FROM packages p LEFT JOIN (SELECT pa.package_id, pa.value_id FROM pkg_annotation pa JOIN annotation t ON t.annotation_id = pa.tag_id AND t.annotation = 'repository') pr ON pr.package_id = p.id LEFT JOIN annotation v ON v.annotation_id = pr.value_id;"}; @@ -32,7 +37,7 @@ if (ret) { -@@ -52,11 +60,23 @@ +@@ -52,11 +65,23 @@ }; } @@ -59,7 +64,7 @@ if (ret) { -@@ -64,11 +84,11 @@ +@@ -64,11 +89,11 @@ { ret, std::system_category(), @@ -73,7 +78,7 @@ info["ram_free"] = ramFree; info["ram_usage"] = 100 - (100 * ramFree / ramTotal); } -@@ -96,7 +116,43 @@ +@@ -96,7 +121,43 @@ static std::string getSerialNumber() { @@ -118,7 +123,7 @@ } static int getCpuCores() -@@ -184,8 +240,12 @@ +@@ -184,8 +245,12 @@ nlohmann::json SysInfo::getProcessesInfo() const { @@ -133,7 +138,7 @@ } nlohmann::json SysInfo::getOsInfo() const -@@ -196,11 +256,12 @@ +@@ -196,11 +261,12 @@ if (!spParser->parseUname(Utils::exec("uname -r"), ret)) { @@ -147,7 +152,7 @@ if (uname(&uts) >= 0) { ret["sysname"] = uts.sysname; -@@ -215,44 +276,257 @@ +@@ -215,44 +281,260 @@ nlohmann::json SysInfo::getPorts() const { @@ -164,23 +169,19 @@ -void SysInfo::getProcessesInfo(std::function /*callback*/) const -{ - // Currently not supported for this OS. --} + if (!query.empty()) + { + nlohmann::json portsjson; + portsjson = nlohmann::json::parse(query); + auto &portsResult = portsjson["sockstat"]["socket"]; - --void SysInfo::getPackages(std::function callback) const --{ -- const auto query{Utils::exec(R"(pkg query -a "%n|%m|%v|%q|%c")")}; ++ + for(auto &port : portsResult) { + std::string localip = ""; + std::string localport = ""; + std::string remoteip = ""; + std::string remoteport = ""; + std::string statedata = ""; - ++ + if (port["pid"] != nullptr) { + + localip = port["local"]["address"]; @@ -228,32 +229,16 @@ +#else + const auto query{Utils::exec(R"(sockstat -46qs)")}; + - if (!query.empty()) - { -- const auto lines{Utils::split(query, '\n')}; ++ if (!query.empty()) ++ { + const auto lines{Utils::split(Utils::trimToOneSpace(query), '\n')}; - ++ + std::regex expression(R"(^(\S+)\s+(\S+)\s+(\d+)\s+(\d+)\s*(\S+)\s+(\S+)\s+(\S+)(?:\s+(\S+))?\s*$)"); + - for (const auto& line : lines) - { -- const auto data{Utils::split(line, '|')}; -- nlohmann::json package; ++ for (const auto& line : lines) ++ { + std::smatch data; - -- package["name"] = data[0]; -- package["vendor"] = data[1]; -- package["version"] = data[2]; -- package["install_time"] = UNKNOWN_VALUE; -- package["location"] = UNKNOWN_VALUE; -- package["architecture"] = data[3]; -- package["groups"] = UNKNOWN_VALUE; -- package["description"] = data[4]; -- package["size"] = 0; -- package["priority"] = UNKNOWN_VALUE; -- package["source"] = UNKNOWN_VALUE; -- package["format"] = "pkg"; -- // The multiarch field won't have a default value ++ + if (std::regex_search(line, data, expression)) + { + std::string localip = ""; @@ -261,8 +246,7 @@ + std::string remoteip = ""; + std::string remoteport = ""; + std::string statedata = ""; - -- callback(package); ++ + auto localdata{Utils::split(data[6], ':')}; + auto remotedata{Utils::split(data[7], ':')}; + @@ -315,21 +299,28 @@ + } +#endif + return ports; -+} -+ + } + +-void SysInfo::getPackages(std::function callback) const +void SysInfo::getProcessesInfo(std::function callback) const -+{ + { +- const auto query{Utils::exec(R"(pkg query -a "%n|%m|%v|%q|%c")")}; + const auto query{Utils::exec(R"(ps -ax -w -o pid,comm,state,ppid,usertime,systime,user,ruser,svuid,group,rgroup,svgid,pri,nice,ssiz,vsz,rss,pmem,etimes,sid,pgid,tpgid,tty,cpu,nlwp,args --libxo json)")}; -+ -+ if (!query.empty()) -+ { + + if (!query.empty()) + { +- const auto lines{Utils::split(query, '\n')}; + nlohmann::json psjson; ++ int64_t agenttime = {Utils::getSecondsFromEpoch()}; ++ int64_t etimes{0}; + psjson = nlohmann::json::parse(query); + auto &processes = psjson["process-information"]["process"]; -+ + +- for (const auto& line : lines) + for(auto &process : processes) { + std::string user_time{""}; + std::string system_time{""}; ++ etimes = std::stoll(process["elapsed-times"].get()); + + user_time = process["user-time"].get(); + system_time = process["system-time"].get(); @@ -356,7 +347,7 @@ + jsProcessInfo["vm_size"] = process["virtual-size"].get(); + jsProcessInfo["resident"] = process["rss"].get(); + //jsProcessInfo["share"] = process["percent-memory"].get(); -+ jsProcessInfo["start_time"] = process["elapsed-times"].get() == "-" ? "0" : process["elapsed-times"].get(); ++ jsProcessInfo["start_time"] = agenttime - etimes; + jsProcessInfo["pgrp"] = process["process-group"].get(); + jsProcessInfo["session"] = process["sid"].get(); + jsProcessInfo["tgid"] = process["terminal-process-gid"].get(); @@ -374,15 +365,31 @@ + if (Utils::existsRegular(PKG_DB_PATHNAME)) + { + try -+ { + { +- const auto data{Utils::split(line, '|')}; +- nlohmann::json package; + std::shared_ptr sqliteConnection = std::make_shared(PKG_DB_PATHNAME, SQLITE_OPEN_READONLY); -+ + +- package["name"] = data[0]; +- package["vendor"] = data[1]; +- package["version"] = data[2]; +- package["install_time"] = UNKNOWN_VALUE; +- package["location"] = UNKNOWN_VALUE; +- package["architecture"] = data[3]; +- package["groups"] = UNKNOWN_VALUE; +- package["description"] = data[4]; +- package["size"] = 0; +- package["priority"] = UNKNOWN_VALUE; +- package["source"] = UNKNOWN_VALUE; +- package["format"] = "pkg"; +- // The multiarch field won't have a default value + SQLite::Statement stmt + { + sqliteConnection, + PKG_QUERY + }; -+ + +- callback(package); + while (SQLITE_ROW == stmt.step()) + { + try @@ -432,3 +439,196 @@ } } +@@ -264,14 +546,188 @@ + + nlohmann::json SysInfo::getGroups() const + { +- //TODO: Pending implementation. +- return nlohmann::json(); ++ nlohmann::json result; ++ GroupsProvider groupsProvider; ++ UserGroupsProvider userGroupsProvider; ++ ++ auto collectedGroups = groupsProvider.collect({}); ++ ++ for (auto& group : collectedGroups) ++ { ++ nlohmann::json groupItem {}; ++ ++ groupItem["group_id"] = group["gid"]; ++ groupItem["group_name"] = (group.contains("groupname") && !group["groupname"].get().empty()) ? group["groupname"] : UNKNOWN_VALUE; ++ groupItem["group_description"] = UNKNOWN_VALUE; ++ groupItem["group_id_signed"] = group["gid_signed"]; ++ groupItem["group_uuid"] = UNKNOWN_VALUE; ++ groupItem["group_is_hidden"] = 0; ++ ++ std::set gids {static_cast(group["gid"].get())}; ++ auto collectedUsersGroups = userGroupsProvider.getUserNamesByGid(gids); ++ ++ if (collectedUsersGroups.empty()) ++ { ++ groupItem["group_users"] = UNKNOWN_VALUE; ++ } ++ else ++ { ++ std::string usersConcatenated; ++ ++ for (const auto& user : collectedUsersGroups) ++ { ++ if (!usersConcatenated.empty()) ++ { ++ usersConcatenated += secondaryArraySeparator; ++ } ++ ++ usersConcatenated += user.get(); ++ } ++ ++ groupItem["group_users"] = usersConcatenated; ++ } ++ ++ result.push_back(std::move(groupItem)); ++ ++ } ++ ++ return result; + } + + nlohmann::json SysInfo::getUsers() const + { +- //TODO: Pending implementation. +- return nlohmann::json(); ++ nlohmann::json result; ++ ++ UsersProvider usersProvider; ++ auto collectedUsers = usersProvider.collect(); ++ ++ LoggedInUsersProvider loggedInUserProvider; ++ auto collectedLoggedInUser = loggedInUserProvider.collect(); ++ ++ UserGroupsProvider userGroupsProvider; ++ ++ for (auto& user : collectedUsers) ++ { ++ nlohmann::json userItem {}; ++ ++ std::string username = (user.contains("username") && !user["username"].get().empty()) ? user["username"] : UNKNOWN_VALUE; ++ ++ userItem["user_id"] = user["uid"]; ++ userItem["user_full_name"] = user["description"]; ++ userItem["user_home"] = user["directory"]; ++ userItem["user_is_remote"] = user["include_remote"]; ++ userItem["user_name"] = username; ++ userItem["user_shell"] = user["shell"]; ++ userItem["user_uid_signed"] = user["uid_signed"]; ++ userItem["user_group_id_signed"] = user["gid_signed"]; ++ userItem["user_group_id"] = user["gid"]; ++ ++ std::set uid {static_cast(user["uid"].get())}; ++ auto collectedUsersGroups = userGroupsProvider.getGroupNamesByUid(uid); ++ ++ if (collectedUsersGroups.empty()) ++ { ++ userItem["user_groups"] = UNKNOWN_VALUE; ++ } ++ else ++ { ++ std::string accumGroups; ++ ++ for (const auto& group : collectedUsersGroups) ++ { ++ if (!accumGroups.empty()) ++ { ++ accumGroups += secondaryArraySeparator; ++ } ++ ++ accumGroups += group.get(); ++ } ++ ++ userItem["user_groups"] = accumGroups; ++ } ++ ++ // Only in windows ++ userItem["user_type"] = UNKNOWN_VALUE; ++ ++ // Macos or windows ++ userItem["user_uuid"] = UNKNOWN_VALUE; ++ ++ // Macos ++ userItem["user_is_hidden"] = 0; ++ userItem["user_created"] = 0; ++ userItem["user_auth_failed_count"] = 0; ++ userItem["user_auth_failed_timestamp"] = 0; ++ ++ auto matched = false; ++ auto lastLogin = 0; ++ ++ userItem["host_ip"] = UNKNOWN_VALUE; ++ ++ //TODO: Avoid this iteration, move logic to LoggedInUsersProvider ++ for (auto& item : collectedLoggedInUser) ++ { ++ // By default, user is not logged in. ++ userItem["login_status"] = 0; ++ ++ // tty,host,time and pid can take more than one value due to different logins. ++ if (item["user"] == username) ++ { ++ matched = true; ++ userItem["login_status"] = 1; ++ ++ auto newDate = item["time"].get(); ++ ++ if (newDate > lastLogin) ++ { ++ lastLogin = newDate; ++ userItem["user_last_login"] = newDate; ++ userItem["login_tty"] = item["tty"].get(); ++ userItem["login_type"] = item["type"].get(); ++ userItem["process_pid"] = item["pid"].get(); ++ } ++ ++ const auto& hostStr = item["host"].get_ref(); ++ ++ if (!hostStr.empty()) ++ { ++ userItem["host_ip"] = userItem["host_ip"].get() == UNKNOWN_VALUE ++ ? hostStr ++ : (userItem["host_ip"].get() + primaryArraySeparator + hostStr); ++ } ++ } ++ } ++ ++ if (!matched) ++ { ++ userItem["login_status"] = 0; ++ userItem["login_tty"] = UNKNOWN_VALUE; ++ userItem["login_type"] = UNKNOWN_VALUE; ++ userItem["process_pid"] = 0; ++ userItem["user_last_login"] = 0; ++ } ++ ++ matched = false; ++ ++ if (!matched) ++ { ++ userItem["user_password_expiration_date"] = 0; ++ userItem["user_password_hash_algorithm"] = UNKNOWN_VALUE; ++ userItem["user_password_inactive_days"] = 0; ++ userItem["user_password_last_change"] = 0; ++ userItem["user_password_max_days_between_changes"] = 0; ++ userItem["user_password_min_days_between_changes"] = 0; ++ userItem["user_password_status"] = UNKNOWN_VALUE; ++ userItem["user_password_warning_days_before_expiration"] = 0; ++ } ++ ++ ++ // By default, user is not sudoer. ++ userItem["user_roles"] = UNKNOWN_VALUE; ++ ++ result.push_back(std::move(userItem)); ++ } ++ ++ return result; + } + + nlohmann::json SysInfo::getServices() const diff --git a/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources-CMakeLists.txt b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources-CMakeLists.txt new file mode 100644 index 000000000000..458b05f1655e --- /dev/null +++ b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources-CMakeLists.txt @@ -0,0 +1,18 @@ +--- src/data_provider/src/extended_sources/CMakeLists.txt 2025-11-07 08:46:03.000000000 +0000 ++++ src/data_provider/src/extended_sources/CMakeLists.txt 2026-01-13 15:00:46.789677000 +0000 +@@ -1,6 +1,11 @@ + include_directories(wrappers) + +-add_subdirectory(groups) +-add_subdirectory(services) +-add_subdirectory(users) +-add_subdirectory(browser_extensions) ++if(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") ++ add_subdirectory(groups) ++ add_subdirectory(users) ++else() ++ add_subdirectory(groups) ++ add_subdirectory(services) ++ add_subdirectory(users) ++ add_subdirectory(browser_extensions) ++endif() diff --git a/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups-CMakeLists.txt b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups-CMakeLists.txt new file mode 100644 index 000000000000..0f34a531e850 --- /dev/null +++ b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups-CMakeLists.txt @@ -0,0 +1,22 @@ +--- src/data_provider/src/extended_sources/groups/CMakeLists.txt 2026-01-13 15:01:19.871247000 +0000 ++++ src/data_provider/src/extended_sources/groups/CMakeLists.txt 2026-01-13 15:07:52.828437000 +0000 +@@ -18,6 +18,8 @@ + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../wrappers/unix/linux) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../wrappers/unix/darwin) ++ elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") ++ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../wrappers/unix/freebsd) + endif() + endif() + +@@ -35,6 +37,10 @@ + list(APPEND SRC_FILES + src/groups_linux.cpp + src/user_groups_linux.cpp) ++ elseif(CMAKE_SYSTEM_NAME STREQUAL "FreeBSD") ++ list(APPEND SRC_FILES ++ src/groups_freebsd.cpp ++ src/user_groups_freebsd.cpp) + elseif(CMAKE_SYSTEM_NAME STREQUAL "Darwin") + find_library(OPEN_DIRECTORY OpenDirectory) + find_library(FOUNDATION Foundation) diff --git a/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_include-groups_freebsd.hpp b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_include-groups_freebsd.hpp new file mode 100644 index 000000000000..a756e0bb02f7 --- /dev/null +++ b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_include-groups_freebsd.hpp @@ -0,0 +1,46 @@ +--- /dev/null 2026-01-13 23:10:56.926889000 +0000 ++++ src/data_provider/src/extended_sources/groups/include/groups_freebsd.hpp 2026-01-13 22:43:51.149789000 +0000 +@@ -0,0 +1,43 @@ ++/* Copyright (C) 2015, Wazuh Inc. ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU General Public ++ * License (version 2) as published by the FSF - Free Software ++ * Foundation. ++ */ ++ ++#pragma once ++ ++#include ++ ++#include "json.hpp" ++#include "igroup_wrapper.hpp" ++ ++/// @brief Class for collecting group information on FreeBSD systems. ++/// ++/// This class provides methods to collect group information from the ++/// Darwin operating system. It uses the system's group database to retrieve ++/// group details such as group name and GID. The collected data is returned ++/// in JSON format. ++class GroupsProvider ++{ ++ public: ++ explicit GroupsProvider(std::shared_ptr groupWrapper); ++ ++ /// @brief Default destructor. ++ GroupsProvider(); ++ ++ /// @brief Collects group information based on provided group IDs. ++ /// @param gids A set of group IDs to collect information for. ++ /// @return A JSON array containing group information. ++ nlohmann::json collect(const std::set& gids = {}); ++ ++ private: ++ std::shared_ptr m_groupWrapper; ++ ++ /// @brief Adds a group to the results JSON array. ++ /// @param results A reference to the JSON array where the group information will be added. ++ /// @param group A pointer to the group structure containing the group information. ++ void addGroupToResults(nlohmann::json& results, const struct group* group); ++}; diff --git a/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_include-user_groups_freebsd.hpp b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_include-user_groups_freebsd.hpp new file mode 100644 index 000000000000..3fdc455fed39 --- /dev/null +++ b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_include-user_groups_freebsd.hpp @@ -0,0 +1,84 @@ +--- /dev/null 2026-01-13 23:11:31.853795000 +0000 ++++ src/data_provider/src/extended_sources/groups/include/user_groups_freebsd.hpp 2026-01-13 22:43:51.149735000 +0000 +@@ -0,0 +1,81 @@ ++/* Copyright (C) 2015, Wazuh Inc. ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU General Public ++ * License (version 2) as published by the FSF - Free Software ++ * Foundation. ++ */ ++ ++#pragma once ++ ++#include ++ ++#include "json.hpp" ++#include "igroup_wrapper.hpp" ++#include "ipasswd_wrapper.hpp" ++ ++#define EXPECTED_GROUPS_MAX 64 ++ ++class UserGroupsProvider ++{ ++ public: ++ /// @brief Constructs a UserGroupsProvider with specific wrappers. ++ /// @param groupWrapper A shared pointer to an IGroupWrapperFreeBSD instance for group operations. ++ /// @param passwdWrapper A shared pointer to an IPasswdWrapperFreeBSD instance for passwd operations. ++ /// @param sysWrapper A shared pointer to an ISystemWrapper instance for system operations. ++ explicit UserGroupsProvider(std::shared_ptr groupWrapper, ++ std::shared_ptr passwdWrapper); ++ ++ /// @brief Default constructor that initializes the UserGroupsProvider with default wrappers. ++ /// @note This constructor uses default implementations of IGroupWrapperFreeBSD, and IPasswdWrapperFreeBSD. ++ UserGroupsProvider(); ++ ++ /// @brief Collects user groups information. ++ /// @param uids A set of user IDs (UIDs) to filter the results. If empty, all users are collected. ++ /// @return A JSON array containing user groups information, where each entry includes UID, GID, and group details. ++ nlohmann::json collect(const std::set& uids = {}); ++ ++ /// @brief Retrieves group names associated with the specified UIDs. ++ /// @param uids A set of user IDs (UIDs) for which to retrieve group names. ++ /// @return A JSON object where keys are UIDs and values are arrays of group names associated with those UIDs. ++ /// If a UID has no associated groups, the value will be an empty array. ++ /// @note This method is useful for quickly mapping UIDs to their group names without retrieving full group details. ++ /// @note If `uids` is empty, it retrieves group names for all users. ++ nlohmann::json getGroupNamesByUid(const std::set& uids = {}); ++ ++ /// @brief Retrieves usernames associated with the specified GIDs. ++ /// @param gids A set of group IDs (GIDs) for which to retrieve usernames. ++ /// @return A JSON object where keys are GIDs and values are arrays of usernames associated with those GIDs. ++ /// If a GID has no associated usernames, the value will be an empty array. ++ /// @note This method is useful for quickly mapping GIDs to their usernames without retrieving full user details. ++ /// @note If `gids` is empty, it retrieves usernames for all groups. ++ nlohmann::json getUserNamesByGid(const std::set& gids = {}); ++ ++ private: ++ std::shared_ptr m_groupWrapper; ++ std::shared_ptr m_passwdWrapper; ++ ++ /// @brief Structure to hold user information. ++ struct UserInfo ++ { ++ const char* name; ++ uid_t uid; ++ gid_t gid; ++ }; ++ ++ /// @brief Retrieves groups for each user and returns a vector of pairs containing UID and their associated groups. ++ /// @param uids A set of user IDs (UIDs) to filter the results. If empty, all users are processed. ++ /// @return A vector of pairs, where each pair contains a UID and a vector of GIDs representing the groups associated with that UID. ++ /// @note This method is used internally to gather user-group associations before formatting the results into JSON. ++ /// @note If a user has no associated groups, the vector of GIDs will be empty. ++ std::vector>> getUserGroups(const std::set& uids); ++ ++ /// @brief Adds groups to the results JSON array for a specific user. ++ /// @param results A reference to the JSON array where the group information will be added. ++ /// @param uid The user ID for which the groups are being added. ++ /// @param groups A pointer to an array of group IDs (GIDs) associated with the user. ++ /// @param ngroups The number of groups in the `groups` array. ++ /// @note This method formats the group information into JSON objects and appends them to the results array. ++ void addGroupsToResults(nlohmann::json& results, uid_t uid, const gid_t* groups, int ngroups); ++}; diff --git a/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_src-groups_freebsd.hpp b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_src-groups_freebsd.hpp new file mode 100644 index 000000000000..d65c4b178507 --- /dev/null +++ b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_src-groups_freebsd.hpp @@ -0,0 +1,95 @@ +--- /dev/null 2026-01-13 23:12:14.070467000 +0000 ++++ src/data_provider/src/extended_sources/groups/src/groups_freebsd.cpp 2026-01-13 22:43:51.150488000 +0000 +@@ -0,0 +1,92 @@ ++/* Copyright (C) 2015, Wazuh Inc. ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU General Public ++ * License (version 2) as published by the FSF - Free Software ++ * Foundation. ++ */ ++ ++#include "groups_freebsd.hpp" ++#include "group_wrapper.hpp" ++ ++constexpr size_t MAX_GETPW_R_BUF_SIZE = 16 * 1024; ++ ++GroupsProvider::GroupsProvider(std::shared_ptr groupWrapper) ++ : m_groupWrapper(std::move(groupWrapper)) {} ++ ++GroupsProvider::GroupsProvider() ++ : m_groupWrapper(std::make_shared()) {} ++ ++nlohmann::json GroupsProvider::collect(const std::set& gids) ++{ ++ nlohmann::json results = nlohmann::json::array(); ++ struct group* groupResult ++ { ++ nullptr ++ }; ++ struct group group; ++ ++ size_t bufSize = MAX_GETPW_R_BUF_SIZE; ++ auto buf = std::make_unique(bufSize); ++ ++ if (!gids.empty()) ++ { ++ for (const auto& gid : gids) ++ { ++ while (m_groupWrapper->getgrgid_r(gid, &group, buf.get(), bufSize, &groupResult) == ERANGE) ++ { ++ bufSize *= 2; ++ buf = std::make_unique(bufSize); ++ } ++ ++ if (groupResult == nullptr) ++ { ++ continue; ++ } ++ ++ addGroupToResults(results, groupResult); ++ } ++ } ++ else ++ { ++ std::set groupsIn; ++ m_groupWrapper->setgrent(); ++ ++ while (1) ++ { ++ while (m_groupWrapper->getgrent_r(&group, buf.get(), bufSize, &groupResult) == ERANGE) ++ { ++ bufSize *= 2; ++ buf = std::make_unique(bufSize); ++ } ++ ++ if (groupResult == nullptr) ++ { ++ break; ++ } ++ ++ if (std::find(groupsIn.begin(), groupsIn.end(), groupResult->gr_gid) == groupsIn.end()) ++ { ++ addGroupToResults(results, groupResult); ++ groupsIn.insert(groupResult->gr_gid); ++ } ++ } ++ ++ m_groupWrapper->endgrent(); ++ groupsIn.clear(); ++ } ++ ++ return results; ++} ++ ++void GroupsProvider::addGroupToResults(nlohmann::json& results, const group* group) ++{ ++ nlohmann::json groupJson; ++ ++ groupJson["groupname"] = group->gr_name; ++ groupJson["gid"] = group->gr_gid; ++ groupJson["gid_signed"] = static_cast(group->gr_gid); ++ ++ results.push_back(groupJson); ++} diff --git a/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_src-user_groups_freebsd.hpp b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_src-user_groups_freebsd.hpp new file mode 100644 index 000000000000..ef6229d3ef82 --- /dev/null +++ b/security/wazuh-agent/files/patch-src_data__provider_src_extended__sources_groups_src-user_groups_freebsd.hpp @@ -0,0 +1,262 @@ +--- /dev/null 2026-01-13 23:12:49.062343000 +0000 ++++ src/data_provider/src/extended_sources/groups/src/user_groups_freebsd.cpp 2026-01-13 22:43:51.150552000 +0000 +@@ -0,0 +1,259 @@ ++/* Copyright (C) 2015, Wazuh Inc. ++ * All rights reserved. ++ * ++ * This program is free software; you can redistribute it ++ * and/or modify it under the terms of the GNU General Public ++ * License (version 2) as published by the FSF - Free Software ++ * Foundation. ++ */ ++ ++#include ++#include ++#include "user_groups_freebsd.hpp" ++#include "group_wrapper.hpp" ++#include "passwd_wrapper.hpp" ++ ++constexpr size_t MAX_GETPW_R_BUF_SIZE = 16 * 1024; ++ ++UserGroupsProvider::UserGroupsProvider(std::shared_ptr groupWrapper, ++ std::shared_ptr passwdWrapper) ++ : m_groupWrapper(std::move(groupWrapper)) ++ , m_passwdWrapper(std::move(passwdWrapper)) ++{ ++} ++ ++UserGroupsProvider::UserGroupsProvider() ++ : m_groupWrapper(std::make_shared()) ++ , m_passwdWrapper(std::make_shared()) ++{ ++} ++ ++nlohmann::json UserGroupsProvider::collect(const std::set& uids) ++{ ++ nlohmann::json results = nlohmann::json::array(); ++ auto usersGroups = getUserGroups(uids); ++ ++ for (const auto& [uid, groups] : usersGroups) ++ { ++ addGroupsToResults(results, uid, groups.data(), static_cast(groups.size())); ++ } ++ ++ return results; ++} ++ ++nlohmann::json UserGroupsProvider::getGroupNamesByUid(const std::set& uids) ++{ ++ const bool singleUid = (uids.size() == 1); ++ nlohmann::json result = singleUid ? nlohmann::json::array() : nlohmann::json::object(); ++ auto usersGroups = getUserGroups(uids); ++ ++ size_t bufSize = sysconf(_SC_GETGR_R_SIZE_MAX); ++ ++ if (bufSize > MAX_GETPW_R_BUF_SIZE) ++ { ++ bufSize = MAX_GETPW_R_BUF_SIZE; ++ } ++ ++ for (const auto& [uid, groups] : usersGroups) ++ { ++ nlohmann::json groupNames = nlohmann::json::array(); ++ ++ for (const auto& gid : groups) ++ { ++ struct group grp; ++ struct group* grpResult = nullptr; ++ auto groupBuf = std::make_unique(bufSize); ++ ++ if (m_groupWrapper->getgrgid_r(gid, &grp, groupBuf.get(), bufSize, &grpResult) == 0 && grpResult != nullptr) ++ { ++ groupNames.push_back(grpResult->gr_name); ++ } ++ } ++ ++ if (singleUid) ++ { ++ result = groupNames; ++ } ++ else ++ { ++ result[std::to_string(uid)] = groupNames; ++ } ++ } ++ ++ return result; ++} ++ ++nlohmann::json UserGroupsProvider::getUserNamesByGid(const std::set& gids) ++{ ++ const bool allGroups = gids.empty(); ++ const bool singleGid = (!allGroups && gids.size() == 1); ++ nlohmann::json result = singleGid ? nlohmann::json::array() : nlohmann::json::object(); ++ ++ size_t bufSize = sysconf(_SC_GETPW_R_SIZE_MAX); ++ ++ if (bufSize > MAX_GETPW_R_BUF_SIZE) ++ { ++ bufSize = MAX_GETPW_R_BUF_SIZE; ++ } ++ ++ std::map> gidToUsernames; ++ ++ if (allGroups) ++ { ++ struct group* grp = nullptr; ++ m_groupWrapper->setgrent(); ++ ++ while ((grp = m_groupWrapper->getgrent()) != nullptr) ++ { ++ gid_t gid = grp->gr_gid; ++ char** members = grp->gr_mem; ++ ++ while (members && *members) ++ { ++ gidToUsernames[gid].insert(*members); ++ ++members; ++ } ++ } ++ ++ m_groupWrapper->endgrent(); ++ } ++ else ++ { ++ for (const auto& gid : gids) ++ { ++ struct group grp; ++ struct group* grpResult = nullptr; ++ auto groupBuf = std::make_unique(bufSize); *** 863 LINES SKIPPED ***