1/* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2/* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5
6#include "nsSandboxViolationSink.h"
7
8#import <Foundation/NSObjCRuntime.h>
9
10#include <unistd.h>
11#include <time.h>
12#include <asl.h>
13#include <dispatch/dispatch.h>
14#include <notify.h>
15#include "mozilla/Preferences.h"
16#include "mozilla/Sprintf.h"
17
18int nsSandboxViolationSink::mNotifyToken = 0;
19uint64_t nsSandboxViolationSink::mLastMsgReceived = 0;
20
21void nsSandboxViolationSink::Start() {
22  if (mNotifyToken) {
23    return;
24  }
25  notify_register_dispatch(
26      SANDBOX_VIOLATION_NOTIFICATION_NAME, &mNotifyToken,
27      dispatch_queue_create(SANDBOX_VIOLATION_QUEUE_NAME, DISPATCH_QUEUE_SERIAL), ^(int token) {
28        ViolationHandler();
29      });
30}
31
32void nsSandboxViolationSink::Stop() {
33  if (!mNotifyToken) {
34    return;
35  }
36  notify_cancel(mNotifyToken);
37  mNotifyToken = 0;
38}
39
40// We need to query syslogd to find out what violations occurred, and whether
41// they were "ours".  We can use the Apple System Log facility to do this.
42// Besides calling notify_post("com.apple.sandbox.violation.*"), Apple's
43// sandboxd also reports all sandbox violations (sent to it by the Sandbox
44// kernel extension) to syslogd, which stores them and makes them viewable
45// in the system console.  This is the database we query.
46
47// ViolationHandler() is always called on its own secondary thread.  This
48// makes it unlikely it will interfere with other browser activity.
49
50void nsSandboxViolationSink::ViolationHandler() {
51  aslmsg query = asl_new(ASL_TYPE_QUERY);
52
53  asl_set_query(query, ASL_KEY_FACILITY, "com.apple.sandbox", ASL_QUERY_OP_EQUAL);
54
55  // Only get reports that were generated very recently.
56  char query_time[30] = {0};
57  SprintfLiteral(query_time, "%li", time(NULL) - 2);
58  asl_set_query(query, ASL_KEY_TIME, query_time, ASL_QUERY_OP_NUMERIC | ASL_QUERY_OP_GREATER_EQUAL);
59
60  // This code is easier to test if we don't just track "our" violations,
61  // which are (normally) few and far between.  For example (for the time
62  // being at least) four appleeventsd sandbox violations happen every time
63  // we start the browser in e10s mode.  But it makes sense to default to
64  // only tracking "our" violations.
65  if (mozilla::Preferences::GetBool("security.sandbox.mac.track.violations.oursonly", true)) {
66    // This makes each of our processes log its own violations.  It might
67    // be better to make the chrome process log all the other processes'
68    // violations.
69    char query_pid[20] = {0};
70    SprintfLiteral(query_pid, "%u", getpid());
71    asl_set_query(query, ASL_KEY_REF_PID, query_pid, ASL_QUERY_OP_EQUAL);
72  }
73
74  aslresponse response = asl_search(nullptr, query);
75
76  // Each time ViolationHandler() is called we grab as many messages as are
77  // available.  Otherwise we might not get them all.
78  if (response) {
79    while (true) {
80      aslmsg hit = nullptr;
81      aslmsg found = nullptr;
82      const char* id_str;
83
84      while ((hit = aslresponse_next(response))) {
85        // Record the message id to avoid logging the same violation more
86        // than once.
87        id_str = asl_get(hit, ASL_KEY_MSG_ID);
88        uint64_t id_val = atoll(id_str);
89        if (id_val <= mLastMsgReceived) {
90          continue;
91        }
92        mLastMsgReceived = id_val;
93        found = hit;
94        break;
95      }
96      if (!found) {
97        break;
98      }
99
100      const char* pid_str = asl_get(found, ASL_KEY_REF_PID);
101      const char* message_str = asl_get(found, ASL_KEY_MSG);
102      NSLog(@"nsSandboxViolationSink::ViolationHandler(): id %s, pid %s, message %s", id_str,
103            pid_str, message_str);
104    }
105    aslresponse_free(response);
106  }
107}
108