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

Add team_identifier to macOS software #23766

Merged
merged 12 commits into from
Nov 15, 2024
1 change: 1 addition & 0 deletions changes/8750-add-team_identifier-to-software
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added `team_identifier` signature information to Apple macOS applications to the `/api/latest/fleet/hosts/:id/software` API endpoint.
42 changes: 19 additions & 23 deletions docs/Contributing/Understanding-host-vitals.md
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,6 @@ SELECT
version AS version,
identifier AS extension_id,
browser_type AS browser,
'Browser plugin (Chrome)' AS type,
lucasmrod marked this conversation as resolved.
Show resolved Hide resolved
'chrome_extensions' AS source,
'' AS vendor,
'' AS installed_path
Expand All @@ -500,7 +499,6 @@ WITH cached_users AS (WITH cached_groups AS (select * from groups)
SELECT
name AS name,
version AS version,
'Package (deb)' AS type,
'' AS extension_id,
'' AS browser,
'deb_packages' AS source,
Expand All @@ -514,7 +512,6 @@ UNION
SELECT
package AS name,
version AS version,
'Package (Portage)' AS type,
'' AS extension_id,
'' AS browser,
'portage_packages' AS source,
Expand All @@ -527,7 +524,6 @@ UNION
SELECT
name AS name,
version AS version,
'Package (RPM)' AS type,
'' AS extension_id,
'' AS browser,
'rpm_packages' AS source,
Expand All @@ -540,7 +536,6 @@ UNION
SELECT
name AS name,
version AS version,
'Package (NPM)' AS type,
'' AS extension_id,
'' AS browser,
'npm_packages' AS source,
Expand All @@ -553,7 +548,6 @@ UNION
SELECT
name AS name,
version AS version,
'Browser plugin (Chrome)' AS type,
identifier AS extension_id,
browser_type AS browser,
'chrome_extensions' AS source,
Expand All @@ -566,7 +560,6 @@ UNION
SELECT
name AS name,
version AS version,
'Browser plugin (Firefox)' AS type,
identifier AS extension_id,
'firefox' AS browser,
'firefox_addons' AS source,
Expand All @@ -579,7 +572,6 @@ UNION
SELECT
name AS name,
version AS version,
'Package (Python)' AS type,
'' AS extension_id,
'' AS browser,
'python_packages' AS source,
Expand All @@ -603,7 +595,6 @@ WITH cached_users AS (WITH cached_groups AS (select * from groups)
SELECT
name AS name,
COALESCE(NULLIF(bundle_short_version, ''), bundle_version) AS version,
'Application (macOS)' AS type,
bundle_identifier AS bundle_identifier,
'' AS extension_id,
'' AS browser,
Expand All @@ -616,7 +607,6 @@ UNION
SELECT
name AS name,
version AS version,
'Package (Python)' AS type,
'' AS bundle_identifier,
'' AS extension_id,
'' AS browser,
Expand All @@ -629,7 +619,6 @@ UNION
SELECT
name AS name,
version AS version,
'Browser plugin (Chrome)' AS type,
'' AS bundle_identifier,
identifier AS extension_id,
browser_type AS browser,
Expand All @@ -642,7 +631,6 @@ UNION
SELECT
name AS name,
version AS version,
'Browser plugin (Firefox)' AS type,
'' AS bundle_identifier,
identifier AS extension_id,
'firefox' AS browser,
Expand All @@ -655,7 +643,6 @@ UNION
SELECT
name As name,
version AS version,
'Browser plugin (Safari)' AS type,
'' AS bundle_identifier,
'' AS extension_id,
'' AS browser,
Expand All @@ -668,7 +655,6 @@ UNION
SELECT
name AS name,
version AS version,
'Package (Homebrew)' AS type,
'' AS bundle_identifier,
'' AS extension_id,
'' AS browser,
Expand All @@ -679,9 +665,27 @@ SELECT
FROM homebrew_packages;
```

## software_macos_codesign

- Description: A software override query[^1] to append codesign information to macOS software entries. Requires `fleetd`

- Platforms: darwin

- Discovery query:
```sql
SELECT 1 FROM osquery_registry WHERE active = true AND registry = 'table' AND name = 'codesign'
```

- Query:
```sql
SELECT a.path, c.team_identifier
FROM apps a
JOIN codesign c ON a.path = c.path
```

## software_macos_firefox

- Description: A software override query[^1] to differentiate between Firefox and Firefox ESR on macOS. Requires `fleetd`
- Description: A software override query[^1] to differentiate between Firefox and Firefox ESR on macOS. Requires `fleetd`

- Platforms: darwin

Expand Down Expand Up @@ -709,7 +713,6 @@ WITH app_paths AS (
ELSE 'Firefox.app'
END AS name,
COALESCE(NULLIF(apps.bundle_short_version, ''), apps.bundle_version) AS version,
'Application (macOS)' AS type,
apps.bundle_identifier AS bundle_identifier,
'' AS extension_id,
'' AS browser,
Expand Down Expand Up @@ -740,7 +743,6 @@ WITH cached_users AS (WITH cached_groups AS (select * from groups)
SELECT
name,
version,
'IDE extension (VS Code)' AS type,
'' AS bundle_identifier,
uuid AS extension_id,
'' AS browser,
Expand All @@ -764,7 +766,6 @@ WITH cached_users AS (WITH cached_groups AS (select * from groups)
SELECT
name AS name,
version AS version,
'Program (Windows)' AS type,
'' AS extension_id,
'' AS browser,
'programs' AS source,
Expand All @@ -775,7 +776,6 @@ UNION
SELECT
name AS name,
version AS version,
'Package (Python)' AS type,
'' AS extension_id,
'' AS browser,
'python_packages' AS source,
Expand All @@ -786,7 +786,6 @@ UNION
SELECT
name AS name,
version AS version,
'Browser plugin (IE)' AS type,
'' AS extension_id,
'' AS browser,
'ie_extensions' AS source,
Expand All @@ -797,7 +796,6 @@ UNION
SELECT
name AS name,
version AS version,
'Browser plugin (Chrome)' AS type,
identifier AS extension_id,
browser_type AS browser,
'chrome_extensions' AS source,
Expand All @@ -808,7 +806,6 @@ UNION
SELECT
name AS name,
version AS version,
'Browser plugin (Firefox)' AS type,
identifier AS extension_id,
'firefox' AS browser,
'firefox_addons' AS source,
Expand All @@ -819,7 +816,6 @@ UNION
SELECT
name AS name,
version AS version,
'Package (Chocolatey)' AS type,
'' AS extension_id,
'' AS browser,
'chocolatey_packages' AS source,
Expand Down
1 change: 1 addition & 0 deletions orbit/changes/add-codesign-table
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* Added `codesign` table to provide the "Team identifier" of macOS applications.
100 changes: 100 additions & 0 deletions orbit/pkg/table/codesign/codesign_darwin.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
//go:build darwin
// +build darwin

// Package codesign implements an extension osquery table
// to get signature information of macOS applications.
package codesign

import (
"bufio"
"bytes"
"context"
"errors"
"os/exec"
"strings"

"github.com/osquery/osquery-go/plugin/table"
"github.com/rs/zerolog/log"
)

// Columns is the schema of the table.
func Columns() []table.ColumnDefinition {
return []table.ColumnDefinition{
// path is the absolute path to the app bundle.
// It's required and only supports the equality operator.
table.TextColumn("path"),
// team_identifier is the "Team ID", aka "Signature ID", "Developer ID".
// The value is "" if the app doesn't have a team identifier set.
// (this is the case for example for builtin Apple apps).
//
// See https://developer.apple.com/help/account/manage-your-team/locate-your-team-id/.
table.TextColumn("team_identifier"),
}
}

// Generate is called to return the results for the table at query time.
//
// Constraints for generating can be retrieved from the queryContext.
func Generate(ctx context.Context, queryContext table.QueryContext) ([]map[string]string, error) {
constraints, ok := queryContext.Constraints["path"]
if !ok || len(constraints.Constraints) == 0 {
return nil, errors.New("missing path")
}

var paths []string
for _, constraint := range constraints.Constraints {
if constraint.Operator != table.OperatorEquals {
return nil, errors.New("only supported operator for 'path' is '='")
}
paths = append(paths, constraint.Expression)
}

var rows []map[string]string
for _, path := range paths {
row := map[string]string{
"path": path,
"team_identifier": "",
}
output, err := exec.CommandContext(ctx, "/usr/bin/codesign",
// `codesign --display` does not perform any verification of executables/resources,
// it just parses and displays signature information read from the `Contents` folder.
"--display",
// If we don't set verbose it only prints the executable path.
"--verbose",
path,
).CombinedOutput() // using CombinedOutput because output is in stderr and stdout is empty.
if err != nil {
// Logging as debug to prevent non signed apps to generate a lot of logged errors.
log.Debug().Err(err).Str("output", string(output)).Str("path", path).Msg("codesign --display failed")
rows = append(rows, row)
continue
}
info := parseCodesignOutput(output)
row["team_identifier"] = info.teamIdentifier
rows = append(rows, row)
}

return rows, nil
}

type parsedInfo struct {
teamIdentifier string
}

func parseCodesignOutput(output []byte) parsedInfo {
const teamIdentifierPrefix = "TeamIdentifier="

scanner := bufio.NewScanner(bytes.NewReader(output))
var info parsedInfo
for scanner.Scan() {
line := scanner.Text()
if strings.HasPrefix(line, teamIdentifierPrefix) {
info.teamIdentifier = strings.TrimSpace(strings.TrimPrefix(line, teamIdentifierPrefix))
// "not set" is usually displayed on Apple builtin apps.
if info.teamIdentifier == "not set" {
info.teamIdentifier = ""
}
}
}
return info
}
3 changes: 3 additions & 0 deletions orbit/pkg/table/extension_darwin.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"

"github.com/fleetdm/fleet/v4/orbit/pkg/table/authdb"
"github.com/fleetdm/fleet/v4/orbit/pkg/table/codesign"
"github.com/fleetdm/fleet/v4/orbit/pkg/table/csrutil_info"
"github.com/fleetdm/fleet/v4/orbit/pkg/table/dataflattentable"
"github.com/fleetdm/fleet/v4/orbit/pkg/table/diskutil/apfs"
Expand Down Expand Up @@ -92,6 +93,8 @@ func PlatformTables(opts PluginOpts) ([]osquery.OsqueryPlugin, error) {

// Table for parsing Apple Property List files, which are typically stored in ~/Library/Preferences/
dataflattentable.TablePlugin(log.Logger, dataflattentable.PlistType), // table name is "parse_plist"

table.NewPlugin("codesign", codesign.Columns(), codesign.Generate),
}

// append platform specific tables
Expand Down
25 changes: 25 additions & 0 deletions schema/osquery_fleet_schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -4171,6 +4171,31 @@
"url": "https://fleetdm.com/tables/cis_audit",
"fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/cis_audit.yml"
},
{
"name": "codesign",
"platforms": [
"darwin"
],
"description": "Retrieves codesign information of a given .app path. It doesn't perform (expensive) verification, it just parses the signature from the 'Contents' folder using the \"codesign --display\" command.",
"columns": [
{
"name": "path",
"type": "text",
"required": true,
"description": "Path is the absolute path to the app folder."
},
{
"name": "team_identifier",
"type": "text",
"required": false,
"description": "Unique 10-character string generated by Apple that's assigned to a developer account to sign packages. This value is empty on unsigned applications and built-in Apple applications."
}
],
"notes": "This table is not a core osquery table. It is included as part of Fleet's agent ([fleetd](https://fleetdm.com/docs/get-started/anatomy#fleetd)).",
"evented": false,
"url": "https://fleetdm.com/tables/codesign",
"fleetRepoUrl": "https://github.com/fleetdm/fleet/blob/main/schema/tables/codesign.yml"
},
{
"name": "connected_displays",
"description": "Provides information about the connected displays of the machine.",
Expand Down
15 changes: 15 additions & 0 deletions schema/tables/codesign.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
name: codesign
platforms:
- darwin
description: Retrieves codesign information of a given .app path. It doesn't perform (expensive) verification, it just parses the signature from the 'Contents' folder using the "codesign --display" command.
columns:
- name: path
type: text
required: true
description: Path is the absolute path to the app folder.
- name: team_identifier
type: text
required: false
description: Unique 10-character string generated by Apple that's assigned to a developer account to sign packages. This value is empty on unsigned applications and built-in Apple applications.
notes: This table is not a core osquery table. It is included as part of Fleet's agent ([fleetd](https://fleetdm.com/docs/get-started/anatomy#fleetd)).
evented: false
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package tables

import (
"database/sql"
"fmt"
)

func init() {
MigrationClient.AddMigration(Up_20241110152839, Down_20241110152839)
}

func Up_20241110152839(tx *sql.Tx) error {
if _, err := tx.Exec(`
ALTER TABLE host_software_installed_paths ADD COLUMN team_identifier VARCHAR(10) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci NOT NULL DEFAULT ''`,
); err != nil {
return fmt.Errorf("failed to add team_identifier to host_software_installed_paths table: %w", err)
}
return nil
}

func Down_20241110152839(tx *sql.Tx) error {
return nil
}
Loading
Loading