Source file
src/tawesoft.co.uk/go/drop/drop.go
Documentation:
src/tawesoft.co.uk/go/drop/drop.go
1 package drop
2
3 import (
4 "context"
5 "fmt"
6 "os"
7 "os/exec"
8 "os/signal"
9 "runtime"
10 "syscall"
11 )
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32 func Drop(username string, files ... Inheritable,) (bool, func() []error, error) {
33 if runtime.GOOS == "windows" { panic("drop does not work on Windows") }
34
35
36
37 const supervise = true
38
39 closer := func(files ... Inheritable) []error {
40 errors := make([]error, 0)
41 for _, i := range files {
42 err := i.Close()
43 if err != nil { errors = append(errors, err) }
44 }
45 return errors
46 }
47
48 handles := make([]*os.File, 0, len(files))
49
50 if IsSuperuser() {
51 uid, gid, groups, err := UserLookup(username)
52 if err != nil { return false, nil, fmt.Errorf("user lookup error for %s: %v", username, err) }
53 if uid == 0 { return false, nil, fmt.Errorf("cannot drop to root") }
54
55 defer closer(files...)
56
57
58
59 ugroups := make([]uint32, len(groups))
60 for i, _ := range groups {
61 ugroups[i] = uint32(groups[i])
62 }
63
64 for _, file := range files {
65 handle, err := file.Open()
66 if err != nil {
67 return false, nil, fmt.Errorf("error opening file while privileged: %v", err)
68 }
69 handles = append(handles, handle)
70 }
71
72 args := os.Args
73 cmd := exec.Command(args[0], args[1:]...)
74
75 if supervise {
76 cmd.Stdin = os.Stdin
77 cmd.Stdout = os.Stdout
78 cmd.Stderr = os.Stderr
79 }
80
81 cmd.ExtraFiles = handles
82 cmd.SysProcAttr = &syscall.SysProcAttr{}
83 cmd.SysProcAttr.Credential = &syscall.Credential{
84 Uid: uint32(uid),
85 Gid: uint32(gid),
86 Groups: ugroups,
87 }
88
89
90
91
92 ctx, cancel := context.WithCancel(context.Background())
93 defer cancel()
94
95 go func(ctx context.Context) {
96 sigchan := make(chan os.Signal, 1)
97 signal.Notify(sigchan, syscall.SIGINT, syscall.SIGTERM)
98
99 select {
100 case <- sigchan:
101 case <- ctx.Done():
102 }
103
104 signal.Stop(sigchan)
105 }(ctx)
106
107 err = cmd.Start()
108 if err != nil {
109 return false, nil, fmt.Errorf("error dropping privileges: %v", err)
110 }
111
112 if supervise {
113 err = cmd.Wait()
114 if err != nil {
115 return false, nil, fmt.Errorf("child process exited with error: %v", err)
116 }
117 }
118
119 closer(files...)
120 return true, func() []error { return []error{} }, nil
121 } else {
122
123 for i := 0; i < len(files); i++ {
124 name := files[i].String()
125 handle := os.NewFile(uintptr(3 + i), name)
126 if handle == nil {
127 closer(files...)
128 return false, nil, fmt.Errorf("missing file handle for %s", name)
129 }
130 handles = append(handles, handle)
131 }
132
133 for i := 0; i < len(files); i++ {
134 name := files[i].String()
135 err := files[i].Inherit(handles[i])
136 if err != nil {
137 closer(files...)
138 return false, nil, fmt.Errorf("error inheriting file %s %v", name, err)
139 }
140 }
141
142 return false, func() []error { return closer(files...) }, nil
143 }
144 }
145
View as plain text