Skip to content

Commit

Permalink
Merge branch 'main' into seph/flare
Browse files Browse the repository at this point in the history
  • Loading branch information
directionless authored Nov 7, 2023
2 parents 19c703b + eb97c37 commit ff6a817
Show file tree
Hide file tree
Showing 9 changed files with 162 additions and 3 deletions.
12 changes: 12 additions & 0 deletions cmd/launcher/console_other.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
//go:build !windows
// +build !windows

package main

func attachConsole() error {
return nil
}

func detachConsole() error {
return nil
}
64 changes: 64 additions & 0 deletions cmd/launcher/console_windows.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
//go:build windows
// +build windows

package main

import (
"fmt"
"os"
"syscall"
)

// attachConsole ensures that subsequent output from the process will be
// printed to the user's terminal.
func attachConsole() error {
kernel32 := syscall.NewLazyDLL("kernel32.dll")
attachConsoleProc := kernel32.NewProc("AttachConsole")

// Call AttachConsole, using the console of the parent of the current process
// See: https://learn.microsoft.com/en-us/windows/console/attachconsole
r1, _, err := attachConsoleProc.Call(^uintptr(0))
if r1 == 0 {
return fmt.Errorf("could not call AttachConsole: %w", err)
}

// Set stdout for newly attached console
stdout, err := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE)
if err != nil {
return fmt.Errorf("getting stdout handle: %w", err)
}
os.Stdout = os.NewFile(uintptr(stdout), "stdout")

// Set stderr for newly attached console
stderr, err := syscall.GetStdHandle(syscall.STD_ERROR_HANDLE)
if err != nil {
return fmt.Errorf("getting stderr handle: %w", err)
}
os.Stderr = os.NewFile(uintptr(stderr), "stderr")

// Print an empty line so that our first line of actual output doesn't occur on the same line
// as the command prompt
fmt.Println("")

return nil
}

// detachConsole undos a previous call to attachConsole. It will leave the window
// appearing to hang, so it notifies the user to press enter in order to get
// their command prompt back.
func detachConsole() error {
// Let the user know they have to press enter to get their prompt back
fmt.Println("Press enter to return to your terminal")

// Now, free the console
kernel32 := syscall.NewLazyDLL("kernel32.dll")
freeConsoleProc := kernel32.NewProc("FreeConsole")

// See: https://learn.microsoft.com/en-us/windows/console/freeconsole
r1, _, err := freeConsoleProc.Call()
if r1 == 0 {
return fmt.Errorf("could not call FreeConsole: %w", err)
}

return nil
}
3 changes: 3 additions & 0 deletions cmd/launcher/doctor.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ import (
)

func runDoctor(args []string) error {
attachConsole()
defer detachConsole()

// Doctor assumes a launcher installation (at least partially) exists
// Overriding some of the default values allows options to be parsed making this assumption
launcher.DefaultAutoupdate = true
Expand Down
3 changes: 3 additions & 0 deletions cmd/launcher/flare.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ import (

// runFlare is a command that runs the flare checkup and saves the results locally or uploads them to a server.
func runFlare(args []string) error {
attachConsole()
defer detachConsole()

// Flare assumes a launcher installation (at least partially) exists
// Overriding some of the default values allows options to be parsed making this assumption
// TODO this stuff needs some deeper thinking
Expand Down
3 changes: 3 additions & 0 deletions cmd/launcher/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -261,7 +261,10 @@ func commandUsage(fs *flag.FlagSet, short string) func() {
}

func runVersion(args []string) error {
attachConsole()
version.PrintFull()
detachConsole()

os.Exit(0)
return nil
}
16 changes: 15 additions & 1 deletion cmd/launcher/svc_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -144,11 +144,25 @@ func runWindowsSvc(args []string) error {
}

func runWindowsSvcForeground(args []string) error {
attachConsole()
defer detachConsole()

// Foreground mode is inherently a debug mode. So we start the
// logger in debugging mode, instead of looking at opts.debug
logger := logutil.NewCLILogger(true)
level.Debug(logger).Log("msg", "foreground service start requested (debug mode)")

// Use new logger to write logs to stdout
systemSlogger := new(multislogger.MultiSlogger)
localSlogger := new(multislogger.MultiSlogger)

handler := slog.NewTextHandler(os.Stdout, &slog.HandlerOptions{
AddSource: true,
Level: slog.LevelDebug,
})
localSlogger.AddHandler(handler)
systemSlogger.AddHandler(handler)

opts, err := launcher.ParseOptions("", os.Args[2:])
if err != nil {
level.Info(logger).Log("err", err)
Expand All @@ -161,7 +175,7 @@ func runWindowsSvcForeground(args []string) error {

run := debug.Run

return run(serviceName, &winSvc{logger: logger, opts: opts})
return run(serviceName, &winSvc{logger: logger, slogger: localSlogger, systemSlogger: systemSlogger, opts: opts})
}

type winSvc struct {
Expand Down
55 changes: 55 additions & 0 deletions pkg/dataflatten/jwt.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package dataflatten

import (
"fmt"
"io"
"os"

"github.com/golang-jwt/jwt/v5"
)

// JWTFile adds support for the kolide_jwt table, which allows parsing
// a file containing a JWT. Note that the kolide_jwt table does not handle
// verification - this is a utility table for convenience.
func JWTFile(file string, opts ...FlattenOpts) ([]Row, error) {
return flattenJWT(file, opts...)
}

func flattenJWT(path string, opts ...FlattenOpts) ([]Row, error) {
// for now, make it clear that any data we parse is unverified
results := map[string]interface{}{"verified": false}

jwtFH, err := os.Open(path)
if err != nil {
return nil, fmt.Errorf("unable to access file: %w", err)
}

defer jwtFH.Close()

tokenRaw, err := io.ReadAll(jwtFH)
if err != nil {
return nil, fmt.Errorf("unable to read JWT: %w", err)
}

// attempt decode into the generic (default) MapClaims struct to ensure we capture
// any claims data that might be useful
token, _, err := new(jwt.Parser).ParseUnverified(string(tokenRaw), jwt.MapClaims{})
if err != nil {
return nil, fmt.Errorf("unable to parse JWT: %w", err)
}

claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return nil, fmt.Errorf("JWT has no parseable claims")
}

parsedClaims := map[string]interface{}{}
for k, v := range claims {
parsedClaims[k] = v
}

results["claims"] = parsedClaims
results["header"] = token.Header

return Flatten(results, opts...)
}
4 changes: 2 additions & 2 deletions pkg/debug/shipper/shipper.go
Original file line number Diff line number Diff line change
Expand Up @@ -201,8 +201,8 @@ func signHttpRequest(req *http.Request, body []byte) {
return
}

request.Header.Set(control.HeaderKey, string(pub))
request.Header.Set(control.HeaderSignature, base64.StdEncoding.EncodeToString(sig))
request.Header.Set(headerKey, string(pub))
request.Header.Set(signatureKey, base64.StdEncoding.EncodeToString(sig))
}

sign(agent.LocalDbKeys(), control.HeaderKey, control.HeaderSignature, req)
Expand Down
5 changes: 5 additions & 0 deletions pkg/osquery/tables/dataflattentable/tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const (
PlistType DataSourceType = iota + 1
JsonType
JsonlType
JWTType
ExecType
XmlType
IniType
Expand Down Expand Up @@ -47,6 +48,7 @@ func AllTablePlugins(logger log.Logger) []osquery.OsqueryPlugin {
TablePlugin(logger, IniType),
TablePlugin(logger, PlistType),
TablePlugin(logger, JsonlType),
TablePlugin(logger, JWTType),
}
}

Expand All @@ -73,6 +75,9 @@ func TablePlugin(logger log.Logger, dataSourceType DataSourceType) osquery.Osque
case IniType:
t.flattenFileFunc = dataflatten.IniFile
t.tableName = "kolide_ini"
case JWTType:
t.flattenFileFunc = dataflatten.JWTFile
t.tableName = "kolide_jwt"
default:
panic("Unknown data source type")
}
Expand Down

0 comments on commit ff6a817

Please sign in to comment.