Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Investigate OSLog Framework Integration #453

Open
5 tasks
TomasTurina opened this issue Dec 19, 2024 · 5 comments
Open
5 tasks

Investigate OSLog Framework Integration #453

TomasTurina opened this issue Dec 19, 2024 · 5 comments
Assignees
Labels

Comments

@TomasTurina
Copy link
Member

Description

As a first step towards creating a new logcollector reader to collect ULS logs from macOS, we will investigate how to integrate the OSLog framework, coded in Objective-C, into the Wazuh Agent, coded in C++.

Additionally, we need to understand how this API works in order to validate the configuration options that will be needed and propose a new reader structure that can be integrated as a class in logcollector that inherits from IReader.

Tasks

  1. Review Configuration Options
  • Validate the proposed options and identify potential additional configurations if needed.
  1. Investigate OSLog Framework Integration
  • Analyze the OSLog API and available Objective-C/C++ bridging options.
  • Determine the best approach to create or use a wrapper for integrating OSLog with C++.
  1. Define Runner Behavior
  • Design the macOS Reader's behavior in alignment with the specification of the epic.
  • Determine whether ULS manages log rotation internally or if the Reader must periodically reload logs.

To accomplish these tasks, we will be working on a separate development branch and all relevant information should be documented in this issue, which can then be improved and added to our official documentation once development is complete.

@jr0me
Copy link
Member

jr0me commented Dec 20, 2024

Update

Started looking into OSLog Apple's documentation, set up a project in macOS to try a C wrapper to interoperate between C++ and Objective-C.

@jr0me
Copy link
Member

jr0me commented Dec 23, 2024

Update

From Apple's development documentation:

The unified logging system is available in iOS 10.0 and later, macOS 10.12 and later, tvOS 10.0 and later, and watchOS 3.0 and later. This system supersedes the Apple System Logger (ASL) and Syslog APIs.

The OSLog framework is only available from macOS 10.15+ (Catalina).

@jr0me
Copy link
Member

jr0me commented Dec 24, 2024

Update

From Apple's documentation:

The unified logging system is available in iOS 10.0 and later, macOS 10.12 and later, tvOS 10.0 and later, and watchOS 3.0 and later. This system supersedes the Apple System Logger (ASL) and Syslog APIs.

You can also access log messages programmatically using the OSLog framework.

All logs have two strings to filter messages, subsystrem identifies a large funcitonal area within your app or apps. Category identifies a particular component or module in a give subystem.

Apps may not use that and instead use the default log from Logger/OSLog.

Levels:
Debug (not persisted to disk)
Info (only when collected by the log tool)
Notice (Default)
Error
Fault

All messages are written to memory initially, some are then persisted.

Some messages may be compressed when being stored.

When that data store exceeds a predefined size, the system purges old messages to make room for new ones.

@jr0me
Copy link
Member

jr0me commented Dec 24, 2024

Update

Here's an example C wrapper to work with Obj-C from C++.

// OSLogWrapper.mm
#import <Foundation/Foundation.h>
#import <OSLog/OSLog.h>

extern "C" void LogToOS(const char *subsystem, const char *category, const char *message)
{
  @autoreleasepool
  {
    NSString *nsSubsystem = [NSString stringWithUTF8String:subsystem];
    NSString *nsCategory = [NSString stringWithUTF8String:category];
    NSString *nsMessage = [NSString stringWithUTF8String:message];

    os_log_t logger = os_log_create([nsSubsystem UTF8String], [nsCategory UTF8String]);
    os_log(logger, "%{public}s", [nsMessage UTF8String]);
  }
}

extern "C" void ReadOSLogs(const char *startTimeStr)
{
  @autoreleasepool
  {
    NSError *error = nil;

    OSLogStore *store = [OSLogStore localStoreAndReturnError:&error];
    if (error)
    {
      NSLog(@"Failed to create OSLogStore: %@", error);
      return;
    }

    NSDate *startDate = nil;
    if (startTimeStr)
    {
      NSString *startTime = [NSString stringWithUTF8String:startTimeStr];
      NSDateFormatter *formatter = [[NSDateFormatter alloc] init];
      formatter.dateFormat = @"yyyy-MM-dd HH:mm:ss";
      startDate = [formatter dateFromString:startTime];
    }
    else
    {
      startDate = [NSDate distantPast];
    }

    OSLogPosition *position = [store positionWithDate:startDate];
    NSDate *startTimestamp = [NSDate dateWithTimeIntervalSinceNow:-60 * 60 * 24 * 7];
    OSLogPosition *logPosition = [store positionWithDate:startTimestamp];
    NSPredicate *predicateTime = [NSPredicate predicateWithFormat:@"date >= %@", startTimestamp];
    NSPredicate *predicate =  [NSCompoundPredicate andPredicateWithSubpredicates:@[ predicateTime ]];

    OSLogEnumerator *enumerator =
        [store entriesEnumeratorWithOptions:0 position:logPosition predicate:predicate error:&error];

    if (error)
    {
      NSLog(@"Failed to create OSLogEnumerator: %@", error);
      return;
    }

    OSLogEntry *logEntry;

    NSLog(@"Reading logs...");
    while ((logEntry = [enumerator nextObject]))
    {
      if ([logEntry isKindOfClass:[OSLogEntryLog class]])
      {
        OSLogEntryLog *logEntrys = (OSLogEntryLog *)logEntry;
        NSLog(@"[%@] %@: %@", logEntrys.date, logEntrys.subsystem, logEntrys.composedMessage);
      }
    }
  }
}

In c++:

// main.cpp
#include <iostream>

extern "C" void ReadOSLogs(const char* startTimeStr);
extern "C" void LogToOS(const char* subsystem, const char* category, const char* message);

int main()
{
    const char* subsystem = "com.example.app";
    const char* category = "general";
    const char* message = "Hello, OSLog from C++ 11!";

    std::cout << "Logging to OSLog..." << std::endl;
    LogToOS(subsystem, category, message);
    std::cout << "Done." << std::endl;

    std::cout << "Reading logs with OSLog..." << std::endl;
    ReadOSLogs("2024-12-23 12:00:00");

    return 0;
}

Compiling with clang:

clang++ -std=c++20 -fobjc-arc -o oslog_reader main.cpp OSLogWrapper.mm -framework Foundation -framework oslog

@jr0me
Copy link
Member

jr0me commented Dec 24, 2024

Update

Pushed to POC branch a commit with an example reading OSLog store using C++ directly without a extern C wrap.

master...poc/453-investigate-oslog-framework-integration

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: In progress
Development

When branches are created from issues, their pull requests are automatically linked.

2 participants