/
screenlock.go
159 lines (130 loc) · 3.89 KB
/
screenlock.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
// +build darwin
// Screenlock calls out to osquery to get the screenlock status.
//
// While this could be implemented as a
// `dataflattentable.TablePluginExec` table, instead we have a
// dedicated table. This allows us to have a consistent set of
// columns, and change the implementation as desired. It's also
// simpler to add the `launchctl` functionality.
//
// Getting User Information
//
// This table uses undocumented APIs, There is some discussion at the
// PR adding the table. See
// https://github.com/osquery/osquery/pull/6243
//
// Empirically, it only works when run in the specific user
// context. Furthermore, setting the effective uid (as sudo might) is
// in adequate. Intead, we need to use `launchctl asuser`.
//
// Resulting data is odd. If a user is logged in, even inactive,
// correct data is returned. If a user has not ever configured these
// settings, the default values are returned. If the user has
// configured these settings, _and_ the user is not logged in, no data
// is returned.
package screenlock
import (
"bytes"
"context"
"encoding/json"
"io/ioutil"
"os"
"os/exec"
"os/user"
"strings"
"time"
"github.com/go-kit/kit/log"
"github.com/go-kit/kit/log/level"
"github.com/kolide/osquery-go"
"github.com/kolide/osquery-go/plugin/table"
"github.com/pkg/errors"
)
type Table struct {
client *osquery.ExtensionManagerClient
logger log.Logger
osqueryd string
}
func TablePlugin(client *osquery.ExtensionManagerClient, logger log.Logger, osqueryd string) *table.Plugin {
columns := []table.ColumnDefinition{
table.IntegerColumn("enabled"),
table.IntegerColumn("grace_period"),
table.TextColumn("user"),
}
t := &Table{
client: client,
logger: logger,
osqueryd: osqueryd,
}
return table.NewPlugin("kolide_screenlock", columns, t.generate)
}
func (t *Table) generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
var results []map[string]string
userQ, ok := queryContext.Constraints["user"]
if !ok || len(userQ.Constraints) == 0 {
return nil, errors.New("The kolide_screenlock table requires a user")
}
for _, userConstraint := range userQ.Constraints {
user := userConstraint.Expression
osqueryResults, err := t.osqueryScreenlock(ctx, user)
if err != nil {
continue
}
for _, row := range osqueryResults {
row["user"] = userConstraint.Expression
results = append(results, row)
}
}
return results, nil
}
func (t *Table) osqueryScreenlock(ctx context.Context, username string) ([]map[string]string, error) {
targetUser, err := user.Lookup(username)
if err != nil {
return nil, errors.Wrapf(err, "looking up username %s", username)
}
ctx, cancel := context.WithTimeout(ctx, 5*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx,
"launchctl",
"asuser",
targetUser.Uid,
t.osqueryd,
"--config_path", "/dev/null",
"--disable_events",
"--disable_database",
"--disable_audit",
"--ephemeral",
"-S",
"--json",
"select enabled, grace_period from screenlock",
)
dir, err := ioutil.TempDir("", "osq-screenlock")
if err != nil {
return nil, errors.Wrap(err, "mktemp")
}
defer os.RemoveAll(dir)
if err := os.Chmod(dir, 0755); err != nil {
return nil, errors.Wrap(err, "chmod")
}
cmd.Dir = dir
stdout, stderr := new(bytes.Buffer), new(bytes.Buffer)
cmd.Stdout, cmd.Stderr = stdout, stderr
if err := cmd.Run(); err != nil {
level.Info(t.logger).Log(
"msg", "Error getting screenlock status",
"stderr", strings.TrimSpace(stderr.String()),
"stdout", strings.TrimSpace(stdout.String()),
"err", err,
)
return nil, errors.Wrap(err, "running osquery")
}
var osqueryResults []map[string]string
if err := json.Unmarshal([]byte(stdout.Bytes()), &osqueryResults); err != nil {
level.Info(t.logger).Log(
"msg", "error unmarshalling json",
"err", err,
"stdout", stdout,
)
return nil, errors.Wrap(err, "unmarshalling json")
}
return osqueryResults, nil
}