@@ -15,6 +15,7 @@ import (
15
15
"os"
16
16
"os/exec"
17
17
"os/user"
18
+ "path"
18
19
"path/filepath"
19
20
"regexp"
20
21
"strings"
@@ -25,6 +26,11 @@ import (
25
26
26
27
const ioctlReadTermios = 0x5401
27
28
29
+ const configDirectory = "/etc/paxrat/"
30
+
31
+ var defaultConfigPath = path .Join (configDirectory , "paxrat.conf" )
32
+ var optionalConfigDirectory = path .Join (configDirectory , "conf.d/" )
33
+
28
34
var configvar string
29
35
var testvar bool
30
36
var xattrvar bool
@@ -36,19 +42,20 @@ var nodivertvar bool
36
42
var replacementvar string
37
43
var quietvar bool
38
44
var verbosevar bool
45
+ var configs []Config
39
46
40
47
type Setting struct {
41
48
Flags string `json:"flags"`
42
49
Nonroot bool `json:"nonroot,omitempty"`
43
50
Nodivert bool `json:"nodivert,omitempty"`
44
51
}
52
+
45
53
type Config struct {
46
54
Settings map [string ]Setting
47
55
}
48
56
49
57
var InotifyFlags uint32
50
58
var InotifyDirFlags uint32
51
- var Conf * Config
52
59
var LogWriter * syslog.Writer
53
60
var SyslogError error
54
61
@@ -68,8 +75,7 @@ func init() {
68
75
inotify .IN_MOVED_TO )
69
76
InotifyDirFlags = (inotify .IN_DONT_FOLLOW | inotify .IN_CREATE |
70
77
inotify .IN_DELETE_SELF | inotify .IN_MOVE_SELF | inotify .IN_MOVED_TO )
71
- Conf = new (Config )
72
- flag .StringVar (& configvar , "c" , "/etc/paxrat/paxrat.conf" ,
78
+ flag .StringVar (& configvar , "c" , defaultConfigPath ,
73
79
"Pax flags configuration file" )
74
80
flag .BoolVar (& testvar , "t" , false ,
75
81
"Test the config file and then exit" )
@@ -94,10 +100,11 @@ func init() {
94
100
95
101
}
96
102
97
- func (conf * Config ) readConfig (path string ) error {
103
+ func readConfig (path string ) (* Config , error ) {
104
+ config := new (Config )
98
105
file , err := os .Open (path )
99
106
if err != nil {
100
- log . Fatal ( err )
107
+ return config , err
101
108
}
102
109
scanner := bufio .NewScanner (file )
103
110
out := ""
@@ -111,12 +118,23 @@ func (conf *Config) readConfig(path string) error {
111
118
fmt .Println (line )
112
119
}
113
120
}
114
- var data = & conf .Settings
121
+ var data = & config .Settings
115
122
err = json .Unmarshal ([]byte (out ), data )
116
123
if err != nil {
117
- log . Fatal ( err )
124
+ return config , err
118
125
}
119
- return err
126
+ return config , nil
127
+ }
128
+
129
+ func mergeConfigs () * Config {
130
+ config := new (Config )
131
+ config .Settings = make (map [string ]Setting )
132
+ for _ , conf := range configs {
133
+ for name , setting := range conf .Settings {
134
+ config .Settings [name ] = setting
135
+ }
136
+ }
137
+ return config
120
138
}
121
139
122
140
func pathExists (path string ) bool {
@@ -144,10 +162,11 @@ func validateFlags(flags string) error {
144
162
var err error
145
163
match , _ := regexp .MatchString ("(?i)[^pemrxs]" , flags )
146
164
if match {
147
- err = fmt .Errorf ("Bad characters found in PaX flags: %s" ,
165
+ err = fmt .Errorf ("bad characters found in PaX flags: %s" ,
148
166
flags )
167
+ return err
149
168
}
150
- return err
169
+ return nil
151
170
}
152
171
153
172
func checkEmulTramp (flags string ) string {
@@ -194,6 +213,7 @@ func setFlags(path string, flags string, nonroot, nodivert bool) error {
194
213
}
195
214
err = validateFlags (flags )
196
215
if err != nil {
216
+ err = fmt .Errorf ("Could not set PaX flags on %s - %s" , path , err )
197
217
return err
198
218
}
199
219
supported , err := isXattrSupported ()
@@ -218,7 +238,7 @@ func setFlags(path string, flags string, nonroot, nodivert bool) error {
218
238
return err
219
239
}
220
240
linkUid := fiPath .Sys ().(* syscall.Stat_t ).Uid
221
- // Throw error if nonroot option is not set but the file is owned by a user other than root
241
+ // Report error if nonroot option is not set but the file is owned by a user other than root
222
242
if ! nonroot && linkUid > 0 {
223
243
err = fmt .Errorf (
224
244
"Cannot set PaX flags on %s. Owner of symlink did not match owner of symlink target\n " ,
@@ -241,7 +261,7 @@ func setFlags(path string, flags string, nonroot, nodivert bool) error {
241
261
return err
242
262
}
243
263
targetUid := fiRPath .Sys ().(* syscall.Stat_t ).Uid
244
- // If nonroot is set then throw an error if the owner of the file is different than the owner of the symlink target
264
+ // If nonroot is set then report an error if the owner of the file != owner of the symlink target
245
265
if nonroot && targetUid != linkUid {
246
266
err = fmt .Errorf (
247
267
"Cannot set PaX flags on %s. Owner of symlink did not match owner of symlink target\n " ,
@@ -275,8 +295,8 @@ func setFlagsWatchMode(watcher *inotify.Watcher, path string, flags string, nonr
275
295
return nil
276
296
}
277
297
278
- func setFlagsFromConfig () {
279
- for path , setting := range ( * Conf ) .Settings {
298
+ func setFlagsFromConfig (conf * Config ) {
299
+ for path , setting := range conf .Settings {
280
300
err := setFlags (path , setting .Flags , setting .Nonroot , setting .Nodivert )
281
301
if err != nil {
282
302
log .Println (err )
@@ -359,13 +379,13 @@ func addWatchToClosestPath(watcher *inotify.Watcher, path string) {
359
379
360
380
}
361
381
362
- func initWatcher () (* inotify.Watcher , error ) {
382
+ func initWatcher (config * Config ) (* inotify.Watcher , error ) {
363
383
log .Println ("Initializing paxrat watcher" )
364
384
watcher , err := inotify .NewWatcher ()
365
385
if err != nil {
366
386
return watcher , err
367
387
}
368
- for path , setting := range ( * Conf ) .Settings {
388
+ for path , setting := range config .Settings {
369
389
addWatchToClosestPath (watcher , path )
370
390
err = setFlagsWatchMode (watcher , path , setting .Flags , setting .Nonroot , setting .Nodivert )
371
391
if err != nil {
@@ -376,32 +396,32 @@ func initWatcher() (*inotify.Watcher, error) {
376
396
}
377
397
378
398
// TODO: Resolve some corner cases like watches not set after create, delete, create, move
379
- func runWatcher (watcher * inotify.Watcher ) {
399
+ func runWatcher (watcher * inotify.Watcher , config * Config ) {
380
400
log .Println ("Starting paxrat watcher" )
381
401
for {
382
402
select {
383
403
case ev := <- watcher .Event :
384
404
if ev .Mask == inotify .IN_CREATE {
385
- if _ , ok := (* Conf ).Settings [ev .Name ]; ok {
405
+ if _ , ok := (* config ).Settings [ev .Name ]; ok {
386
406
watcher .AddWatch (ev .Name , InotifyFlags )
387
407
log .Printf ("File created: %s\n " , ev .Name )
388
408
}
389
409
// Catch directory creation events for non-existent directories in executable path
390
410
} else if ev .Mask == (inotify .IN_CREATE | inotify .IN_ISDIR ) {
391
- for path , _ := range (* Conf ).Settings {
411
+ for path , _ := range (* config ).Settings {
392
412
if strings .HasPrefix (path , ev .Name ) {
393
413
addWatchToClosestPath (watcher , path )
394
414
}
395
415
}
396
416
} else if ev .Mask == inotify .IN_DELETE_SELF || ev .Mask == inotify .IN_MOVE_SELF {
397
- if _ , ok := (* Conf ).Settings [ev .Name ]; ok {
417
+ if _ , ok := (* config ).Settings [ev .Name ]; ok {
398
418
log .Printf ("File deleted: %s\n " , ev .Name )
399
419
parent := filepath .Dir (ev .Name )
400
420
watcher .AddWatch (parent , InotifyDirFlags )
401
421
continue
402
422
}
403
423
} else if ev .Mask == inotify .IN_ATTRIB {
404
- if _ , ok := (* Conf ).Settings [ev .Name ]; ok {
424
+ if _ , ok := (* config ).Settings [ev .Name ]; ok {
405
425
exists := pathExists (ev .Name )
406
426
if ! exists {
407
427
log .Printf ("File deleted: %s\n " , ev .Name )
@@ -414,7 +434,7 @@ func runWatcher(watcher *inotify.Watcher) {
414
434
}
415
435
}
416
436
}
417
- if settings , ok := (* Conf ).Settings [ev .Name ]; ok {
437
+ if settings , ok := (* config ).Settings [ev .Name ]; ok {
418
438
if ev .Mask != inotify .IN_IGNORED {
419
439
err := setFlagsWatchMode (watcher , ev .Name , settings .Flags , settings .Nonroot , settings .Nodivert )
420
440
if err != nil {
@@ -436,9 +456,9 @@ func main() {
436
456
}
437
457
if testvar {
438
458
log .Printf ("Reading config from: %s\n " , configvar )
439
- err := Conf . readConfig (configvar )
459
+ _ , err := readConfig (configvar )
440
460
if err != nil {
441
- log .Fatal ( err )
461
+ log .Fatalf ( "Could not read config: %s - %s \n " , configvar , err )
442
462
}
443
463
log .Printf ("Configuration is valid\n " )
444
464
os .Exit (0 )
@@ -449,22 +469,50 @@ func main() {
449
469
}
450
470
os .Exit (0 )
451
471
} else {
472
+ var mergedConfig * Config
452
473
if xattrvar {
453
474
log .Println ("Running forced xattr mode" )
454
475
}
455
476
log .Printf ("Reading config from: %s\n " , configvar )
456
- err := Conf . readConfig (configvar )
477
+ conf , err := readConfig (configvar )
457
478
if err != nil {
458
- log .Fatal (err )
479
+ log .Fatalf ("Could not read config: %s, %s\n " , configvar , err )
480
+ }
481
+ configs = append (configs , (* conf ))
482
+ if configvar == defaultConfigPath &&
483
+ pathExists (optionalConfigDirectory ) {
484
+ files , err := ioutil .ReadDir (optionalConfigDirectory )
485
+ if err != nil {
486
+ log .Printf (
487
+ "Could not read optional config directory: %s - %s\n " ,
488
+ optionalConfigDirectory , err )
489
+ }
490
+ for _ , confFile := range files {
491
+ if ! confFile .IsDir () {
492
+ confFilePath := path .Join (optionalConfigDirectory ,
493
+ confFile .Name ())
494
+ log .Printf ("Reading config from: %s" , confFilePath )
495
+ if pathExists (confFilePath ) {
496
+ conf , err := readConfig (confFilePath )
497
+ if err != nil {
498
+ // Do not die fatally on bad optional config
499
+ log .Printf ("Could not read config: %s - %s\n " ,
500
+ confFile .Name (), err )
501
+ }
502
+ configs = append (configs , (* conf ))
503
+ }
504
+ }
505
+ }
459
506
}
507
+ mergedConfig = mergeConfigs ()
460
508
if watchvar {
461
- watcher , err := initWatcher ()
509
+ watcher , err := initWatcher (mergedConfig )
462
510
if err != nil {
463
511
log .Fatalf ("Could not initialize watcher: %s\n " , err )
464
512
}
465
- runWatcher (watcher )
513
+ runWatcher (watcher , mergedConfig )
466
514
} else {
467
- setFlagsFromConfig ()
515
+ setFlagsFromConfig (mergedConfig )
468
516
os .Exit (0 )
469
517
}
470
518
}
0 commit comments