Compare commits

..

No commits in common. "2ffca6984a03b2daa3bb114f70ea84e71439559a" and "f30a439bcd6aa97fac0f99cdd94381ae025aa64d" have entirely different histories.

7 changed files with 101 additions and 187 deletions

View File

@ -5,7 +5,6 @@ import (
"errors" "errors"
"io" "io"
"os" "os"
"strings"
) )
// ProxyPair is an upstream dbus address and a downstream socket path. // ProxyPair is an upstream dbus address and a downstream socket path.
@ -28,61 +27,6 @@ type Config struct {
Filter bool `json:"filter"` Filter bool `json:"filter"`
} }
func (c *Config) interfaces(yield func(string) bool) {
for _, iface := range c.See {
if !yield(iface) {
return
}
}
for _, iface := range c.Talk {
if !yield(iface) {
return
}
}
for _, iface := range c.Own {
if !yield(iface) {
return
}
}
for iface := range c.Call {
if !yield(iface) {
return
}
}
for iface := range c.Broadcast {
if !yield(iface) {
return
}
}
}
func (c *Config) checkInterfaces(segment string) error {
for iface := range c.interfaces {
/*
xdg-dbus-proxy fails without output when this condition is not met:
char *dot = strrchr (filter->interface, '.');
if (dot != NULL)
{
*dot = 0;
if (strcmp (dot + 1, "*") != 0)
filter->member = g_strdup (dot + 1);
}
trim ".*" since they are removed before searching for '.':
if (g_str_has_suffix (name, ".*"))
{
name[strlen (name) - 2] = 0;
wildcard = TRUE;
}
*/
if strings.IndexByte(strings.TrimSuffix(iface, ".*"), '.') == -1 {
return &BadInterfaceError{iface, segment}
}
}
return nil
}
func (c *Config) Args(bus ProxyPair) (args []string) { func (c *Config) Args(bus ProxyPair) (args []string) {
argc := 2 + len(c.See) + len(c.Talk) + len(c.Own) + len(c.Call) + len(c.Broadcast) argc := 2 + len(c.See) + len(c.Talk) + len(c.Own) + len(c.Call) + len(c.Broadcast)
if c.Log { if c.Log {
@ -119,7 +63,9 @@ func (c *Config) Args(bus ProxyPair) (args []string) {
return return
} }
func (c *Config) Load(r io.Reader) error { return json.NewDecoder(r).Decode(&c) } func (c *Config) Load(r io.Reader) error {
return json.NewDecoder(r).Decode(&c)
}
// NewConfigFromFile opens the target config file at path and parses its contents into *Config. // NewConfigFromFile opens the target config file at path and parses its contents into *Config.
func NewConfigFromFile(path string) (*Config, error) { func NewConfigFromFile(path string) (*Config, error) {

View File

@ -2,7 +2,6 @@ package dbus
import ( import (
"context" "context"
"fmt"
"io" "io"
"os/exec" "os/exec"
"sync" "sync"
@ -15,15 +14,6 @@ import (
// Overriding ProxyName will only affect Proxy instance created after the change. // Overriding ProxyName will only affect Proxy instance created after the change.
var ProxyName = "xdg-dbus-proxy" var ProxyName = "xdg-dbus-proxy"
type BadInterfaceError struct {
Interface string
Segment string
}
func (e *BadInterfaceError) Error() string {
return fmt.Sprintf("bad interface string %q in %s bus configuration", e.Interface, e.Segment)
}
// Proxy holds the state of a xdg-dbus-proxy process, and should never be copied. // Proxy holds the state of a xdg-dbus-proxy process, and should never be copied.
type Proxy struct { type Proxy struct {
helper helper.Helper helper helper.Helper
@ -76,15 +66,9 @@ func Finalise(sessionBus, systemBus ProxyPair, session, system *Config) (final *
var args []string var args []string
if session != nil { if session != nil {
if err = session.checkInterfaces("session"); err != nil {
return
}
args = append(args, session.Args(sessionBus)...) args = append(args, session.Args(sessionBus)...)
} }
if system != nil { if system != nil {
if err = system.checkInterfaces("system"); err != nil {
return
}
args = append(args, system.Args(systemBus)...) args = append(args, system.Args(systemBus)...)
} }

145
nixos.nix
View File

@ -8,10 +8,12 @@ packages:
let let
inherit (lib) inherit (lib)
lists
mkMerge mkMerge
mkIf mkIf
mapAttrs mapAttrs
mergeAttrsList
imap1
foldr
foldlAttrs foldlAttrs
optional optional
optionals optionals
@ -28,27 +30,6 @@ in
imports = [ (import ./options.nix packages) ]; imports = [ (import ./options.nix packages) ];
config = mkIf cfg.enable { config = mkIf cfg.enable {
assertions = [
(
let
conflictingApps = foldlAttrs (
acc: id: app:
(
acc
++ foldlAttrs (
acc': id': app':
if id == id' || app.shareUid && app'.shareUid || app.identity != app'.identity then acc' else acc' ++ [ id ]
) [ ] cfg.apps
)
) [ ] cfg.apps;
in
{
assertion = (lists.length conflictingApps) == 0;
message = "the following fortify apps have conflicting identities: " + (builtins.concatStringsSep ", " conflictingApps);
}
)
];
security.wrappers.fsu = { security.wrappers.fsu = {
source = "${cfg.fsuPackage}/bin/fsu"; source = "${cfg.fsuPackage}/bin/fsu";
setuid = true; setuid = true;
@ -68,19 +49,22 @@ in
home-manager = home-manager =
let let
privPackages = mapAttrs (username: fid: { privPackages = mapAttrs (username: fid: {
home.packages = foldlAttrs ( home.packages =
acc: id: app: let
[ # aid 0 is reserved
( wrappers = imap1 (
aid: app:
let let
extendDBusDefault = id: ext: { extendDBusDefault = id: ext: {
filter = true; filter = true;
talk = [ "org.freedesktop.Notifications" ] ++ ext.talk; talk = [ "org.freedesktop.Notifications" ] ++ ext.talk;
own = [ own =
"${id}.*" (optionals (app.id != null) [
"org.mpris.MediaPlayer2.${id}.*" "${id}.*"
] ++ ext.own; "org.mpris.MediaPlayer2.${id}.*"
])
++ ext.own;
inherit (ext) call broadcast; inherit (ext) call broadcast;
}; };
@ -94,7 +78,7 @@ in
}; };
in in
{ {
session_bus = if app.dbus.session != null then (app.dbus.session (extendDBusDefault id)) else (extendDBusDefault id default); session_bus = if app.dbus.session != null then (app.dbus.session (extendDBusDefault app.id)) else (extendDBusDefault app.id default);
system_bus = app.dbus.system; system_bus = app.dbus.system;
}; };
command = if app.command == null then app.name else app.command; command = if app.command == null then app.name else app.command;
@ -103,6 +87,8 @@ in
isGraphical = if app.gpu != null then app.gpu else app.capability.wayland || app.capability.x11; isGraphical = if app.gpu != null then app.gpu else app.capability.wayland || app.capability.x11;
conf = { conf = {
inherit (app) id;
path = path =
if app.path == null then if app.path == null then
pkgs.writeScript "${app.name}-start" '' pkgs.writeScript "${app.name}-start" ''
@ -113,15 +99,16 @@ in
app.path; app.path;
args = if app.args == null then [ "${app.name}-start" ] else app.args; args = if app.args == null then [ "${app.name}-start" ] else app.args;
inherit id enablements; inherit enablements;
inherit (dbusConfig) session_bus system_bus; inherit (dbusConfig) session_bus system_bus;
direct_wayland = app.insecureWayland; direct_wayland = app.insecureWayland;
username = getsubname fid app.identity; username = getsubname fid aid;
data = getsubhome fid app.identity; data = getsubhome fid aid;
inherit (app) identity groups; identity = aid;
inherit (app) groups;
container = { container = {
inherit (app) inherit (app)
@ -201,9 +188,10 @@ in
pkgs.writeShellScriptBin app.name '' pkgs.writeShellScriptBin app.name ''
exec fortify${if app.verbose then " -v" else ""} app ${pkgs.writeText "fortify-${app.name}.json" (builtins.toJSON conf)} $@ exec fortify${if app.verbose then " -v" else ""} app ${pkgs.writeText "fortify-${app.name}.json" (builtins.toJSON conf)} $@
'' ''
) ) cfg.apps;
] in
++ ( foldr (
app: acc:
let let
pkg = if app.share != null then app.share else pkgs.${app.name}; pkg = if app.share != null then app.share else pkgs.${app.name};
copy = source: "[ -d '${source}' ] && cp -Lrv '${source}' $out/share || true"; copy = source: "[ -d '${source}' ] && cp -Lrv '${source}' $out/share || true";
@ -223,33 +211,30 @@ in
fi fi
'' ''
) )
) ++ acc
++ acc ) (wrappers ++ [ cfg.package ]) cfg.apps;
) [ cfg.package ] cfg.apps;
}) cfg.users; }) cfg.users;
in in
{ {
useUserPackages = false; # prevent users.users entries from being added useUserPackages = false; # prevent users.users entries from being added
users = mkMerge ( users = foldlAttrs (
foldlAttrs ( acc: _: fid:
acc: _: fid: mkMerge [
(mergeAttrsList (
# aid 0 is reserved
imap1 (aid: app: {
${getsubname fid aid} = mkMerge [
cfg.extraHomeConfig
app.extraConfig
{ home.packages = app.packages; }
];
}) cfg.apps
))
{ ${getsubname fid 0} = cfg.extraHomeConfig; }
acc acc
++ foldlAttrs ( ]
acc': _: app: ) privPackages cfg.users;
acc'
++ [
{
${getsubname fid app.identity} = mkMerge [
cfg.extraHomeConfig
app.extraConfig
{ home.packages = app.packages; }
];
}
]
) [ { ${getsubname fid 0} = cfg.extraHomeConfig; } ] cfg.apps
) [ privPackages ] cfg.users
);
}; };
users = users =
@ -265,27 +250,33 @@ in
getgroup = fid: aid: { gid = getsubuid fid aid; }; getgroup = fid: aid: { gid = getsubuid fid aid; };
in in
{ {
users = mkMerge ( users = foldlAttrs (
foldlAttrs ( acc: _: fid:
acc: _: fid: mkMerge [
(mergeAttrsList (
# aid 0 is reserved
imap1 (aid: _: {
${getsubname fid aid} = getuser fid aid;
}) cfg.apps
))
{ ${getsubname fid 0} = getuser fid 0; }
acc acc
++ foldlAttrs ( ]
acc': _: app: ) { } cfg.users;
acc' ++ [ { ${getsubname fid app.identity} = getuser fid app.identity; } ]
) [ { ${getsubname fid 0} = getuser fid 0; } ] cfg.apps
) [ ] cfg.users
);
groups = mkMerge ( groups = foldlAttrs (
foldlAttrs ( acc: _: fid:
acc: _: fid: mkMerge [
(mergeAttrsList (
# aid 0 is reserved
imap1 (aid: _: {
${getsubname fid aid} = getgroup fid aid;
}) cfg.apps
))
{ ${getsubname fid 0} = getgroup fid 0; }
acc acc
++ foldlAttrs ( ]
acc': _: app: ) { } cfg.users;
acc' ++ [ { ${getsubname fid app.identity} = getgroup fid app.identity; } ]
) [ { ${getsubname fid 0} = getgroup fid 0; } ] cfg.apps
) [ ] cfg.users
);
}; };
}; };
} }

View File

@ -76,7 +76,6 @@ in
type = type =
let let
inherit (types) inherit (types)
ints
str str
bool bool
package package
@ -88,7 +87,7 @@ in
functionTo functionTo
; ;
in in
attrsOf (submodule { listOf (submodule {
options = { options = {
name = mkOption { name = mkOption {
type = str; type = str;
@ -99,13 +98,13 @@ in
verbose = mkEnableOption "launchers with verbose output"; verbose = mkEnableOption "launchers with verbose output";
identity = mkOption { id = mkOption {
type = ints.between 1 9999; type = nullOr str;
default = null;
description = '' description = ''
Application identity. Identity 0 is reserved for system services. Freedesktop application ID.
''; '';
}; };
shareUid = mkEnableOption "sharing identity with another application";
packages = mkOption { packages = mkOption {
type = listOf package; type = listOf package;
@ -274,7 +273,7 @@ in
}; };
}; };
}); });
default = { }; default = [ ];
description = '' description = ''
Declaratively configured fortify apps. Declaratively configured fortify apps.
''; '';

View File

@ -99,10 +99,9 @@
home.stateVersion = "23.05"; home.stateVersion = "23.05";
}; };
apps = { apps = [
"cat.gensokyo.extern.foot.noEnablements" = { {
name = "ne-foot"; name = "ne-foot";
identity = 1;
verbose = true; verbose = true;
share = pkgs.foot; share = pkgs.foot;
packages = with pkgs; [ packages = with pkgs; [
@ -116,21 +115,17 @@
dbus = false; dbus = false;
pulse = false; pulse = false;
}; };
}; }
{
"cat.gensokyo.extern.foot.pulseaudio" = {
name = "pa-foot"; name = "pa-foot";
identity = 2;
verbose = true; verbose = true;
share = pkgs.foot; share = pkgs.foot;
packages = [ pkgs.foot ]; packages = [ pkgs.foot ];
command = "foot"; command = "foot";
capability.dbus = false; capability.dbus = false;
}; }
{
"cat.gensokyo.extern.Alacritty.x11" = {
name = "x11-alacritty"; name = "x11-alacritty";
identity = 3;
verbose = true; verbose = true;
share = pkgs.alacritty; share = pkgs.alacritty;
packages = with pkgs; [ packages = with pkgs; [
@ -147,11 +142,9 @@
dbus = false; dbus = false;
pulse = false; pulse = false;
}; };
}; }
{
"cat.gensokyo.extern.foot.directWayland" = {
name = "da-foot"; name = "da-foot";
identity = 4;
verbose = true; verbose = true;
insecureWayland = true; insecureWayland = true;
share = pkgs.foot; share = pkgs.foot;
@ -166,11 +159,9 @@
dbus = false; dbus = false;
pulse = false; pulse = false;
}; };
}; }
{
"cat.gensokyo.extern.strace.wantFail" = {
name = "strace-failure"; name = "strace-failure";
identity = 5;
verbose = true; verbose = true;
share = pkgs.strace; share = pkgs.strace;
command = "strace true"; command = "strace true";
@ -180,7 +171,7 @@
dbus = false; dbus = false;
pulse = false; pulse = false;
}; };
}; }
}; ];
}; };
} }

View File

@ -24,7 +24,7 @@ let
}; };
callTestCase = callTestCase =
path: identity: path:
let let
tc = import path { tc = import path {
inherit inherit
@ -36,7 +36,6 @@ let
in in
{ {
name = "check-sandbox-${tc.name}"; name = "check-sandbox-${tc.name}";
inherit identity;
verbose = true; verbose = true;
inherit (tc) inherit (tc)
tty tty
@ -52,12 +51,10 @@ let
(toString (builtins.toFile "fortify-${tc.name}-want.json" (builtins.toJSON tc.want))) (toString (builtins.toFile "fortify-${tc.name}-want.json" (builtins.toJSON tc.want)))
]; ];
}; };
testCaseName = name: "cat.gensokyo.fortify.test." + name;
in in
{ {
${testCaseName "preset"} = callTestCase ./preset.nix 1; preset = callTestCase ./preset.nix;
${testCaseName "tty"} = callTestCase ./tty.nix 2; tty = callTestCase ./tty.nix;
${testCaseName "mapuid"} = callTestCase ./mapuid.nix 3; mapuid = callTestCase ./mapuid.nix;
${testCaseName "device"} = callTestCase ./device.nix 4; device = callTestCase ./device.nix;
} }

View File

@ -6,6 +6,7 @@
}: }:
let let
testProgram = pkgs.callPackage ./tool/package.nix { inherit (config.environment.fortify.package) version; }; testProgram = pkgs.callPackage ./tool/package.nix { inherit (config.environment.fortify.package) version; };
testCases = import ./case lib testProgram;
in in
{ {
users.users = { users.users = {
@ -75,6 +76,11 @@ in
} }
]; ];
apps = import ./case lib testProgram; apps = with testCases; [
preset
tty
mapuid
device
];
}; };
} }