1*2810ac1bSKiyoung Kimpackage cap 2*2810ac1bSKiyoung Kim 3*2810ac1bSKiyoung Kimimport ( 4*2810ac1bSKiyoung Kim "errors" 5*2810ac1bSKiyoung Kim "os" 6*2810ac1bSKiyoung Kim "runtime" 7*2810ac1bSKiyoung Kim "sync" 8*2810ac1bSKiyoung Kim "syscall" 9*2810ac1bSKiyoung Kim "unsafe" 10*2810ac1bSKiyoung Kim) 11*2810ac1bSKiyoung Kim 12*2810ac1bSKiyoung Kim// Launcher holds a configuration for executing an optional callback 13*2810ac1bSKiyoung Kim// function and/or launching a child process with capability state 14*2810ac1bSKiyoung Kim// different from the parent. 15*2810ac1bSKiyoung Kim// 16*2810ac1bSKiyoung Kim// Note, go1.10 is the earliest version of the Go toolchain that can 17*2810ac1bSKiyoung Kim// support this abstraction. 18*2810ac1bSKiyoung Kimtype Launcher struct { 19*2810ac1bSKiyoung Kim mu sync.RWMutex 20*2810ac1bSKiyoung Kim 21*2810ac1bSKiyoung Kim // Note, path and args must be set, or callbackFn. They cannot 22*2810ac1bSKiyoung Kim // both be empty. In such cases .Launch() will error out. 23*2810ac1bSKiyoung Kim path string 24*2810ac1bSKiyoung Kim args []string 25*2810ac1bSKiyoung Kim env []string 26*2810ac1bSKiyoung Kim 27*2810ac1bSKiyoung Kim callbackFn func(pa *syscall.ProcAttr, data interface{}) error 28*2810ac1bSKiyoung Kim 29*2810ac1bSKiyoung Kim // The following are only honored when path is non empty. 30*2810ac1bSKiyoung Kim changeUIDs bool 31*2810ac1bSKiyoung Kim uid int 32*2810ac1bSKiyoung Kim 33*2810ac1bSKiyoung Kim changeGIDs bool 34*2810ac1bSKiyoung Kim gid int 35*2810ac1bSKiyoung Kim groups []int 36*2810ac1bSKiyoung Kim 37*2810ac1bSKiyoung Kim changeMode bool 38*2810ac1bSKiyoung Kim mode Mode 39*2810ac1bSKiyoung Kim 40*2810ac1bSKiyoung Kim iab *IAB 41*2810ac1bSKiyoung Kim 42*2810ac1bSKiyoung Kim chroot string 43*2810ac1bSKiyoung Kim} 44*2810ac1bSKiyoung Kim 45*2810ac1bSKiyoung Kim// NewLauncher returns a new launcher for the specified program path 46*2810ac1bSKiyoung Kim// and args with the specified environment. 47*2810ac1bSKiyoung Kimfunc NewLauncher(path string, args []string, env []string) *Launcher { 48*2810ac1bSKiyoung Kim return &Launcher{ 49*2810ac1bSKiyoung Kim path: path, 50*2810ac1bSKiyoung Kim args: args, 51*2810ac1bSKiyoung Kim env: env, 52*2810ac1bSKiyoung Kim } 53*2810ac1bSKiyoung Kim} 54*2810ac1bSKiyoung Kim 55*2810ac1bSKiyoung Kim// FuncLauncher returns a new launcher whose purpose is to only 56*2810ac1bSKiyoung Kim// execute fn in a disposable security context. This is a more bare 57*2810ac1bSKiyoung Kim// bones variant of the more elaborate program launcher returned by 58*2810ac1bSKiyoung Kim// cap.NewLauncher(). 59*2810ac1bSKiyoung Kim// 60*2810ac1bSKiyoung Kim// Note, this launcher will fully ignore any overrides provided by the 61*2810ac1bSKiyoung Kim// (*Launcher).SetUID() etc. methods. Should your fn() code want to 62*2810ac1bSKiyoung Kim// run with a different capability state or other privilege, it should 63*2810ac1bSKiyoung Kim// use the cap.*() functions to set them directly. The cap package 64*2810ac1bSKiyoung Kim// will ensure that their effects are limited to the runtime of this 65*2810ac1bSKiyoung Kim// individual function invocation. Warning: executing non-cap.*() 66*2810ac1bSKiyoung Kim// syscall functions may corrupt the state of the program runtime and 67*2810ac1bSKiyoung Kim// lead to unpredictable results. 68*2810ac1bSKiyoung Kim// 69*2810ac1bSKiyoung Kim// The properties of fn are similar to those supplied via 70*2810ac1bSKiyoung Kim// (*Launcher).Callback(fn) method. However, this launcher is bare 71*2810ac1bSKiyoung Kim// bones because, when launching, all privilege management performed 72*2810ac1bSKiyoung Kim// by the fn() is fully discarded when the fn() completes 73*2810ac1bSKiyoung Kim// execution. That is, it does not end by exec()ing some program. 74*2810ac1bSKiyoung Kimfunc FuncLauncher(fn func(interface{}) error) *Launcher { 75*2810ac1bSKiyoung Kim return &Launcher{ 76*2810ac1bSKiyoung Kim callbackFn: func(ignored *syscall.ProcAttr, data interface{}) error { 77*2810ac1bSKiyoung Kim return fn(data) 78*2810ac1bSKiyoung Kim }, 79*2810ac1bSKiyoung Kim } 80*2810ac1bSKiyoung Kim} 81*2810ac1bSKiyoung Kim 82*2810ac1bSKiyoung Kim// Callback changes the callback function for Launch() to call before 83*2810ac1bSKiyoung Kim// changing privilege. The only thing that is assumed is that the OS 84*2810ac1bSKiyoung Kim// thread in use to call this callback function at launch time will be 85*2810ac1bSKiyoung Kim// the one that ultimately calls fork to complete the launch of a path 86*2810ac1bSKiyoung Kim// specified executable. Any returned error value of said function 87*2810ac1bSKiyoung Kim// will terminate the launch process. 88*2810ac1bSKiyoung Kim// 89*2810ac1bSKiyoung Kim// A nil fn causes there to be no callback function invoked during a 90*2810ac1bSKiyoung Kim// Launch() sequence - it will remove any pre-existing callback. 91*2810ac1bSKiyoung Kim// 92*2810ac1bSKiyoung Kim// If the non-nil fn requires any effective capabilities in order to 93*2810ac1bSKiyoung Kim// run, they can be raised prior to calling .Launch() or inside the 94*2810ac1bSKiyoung Kim// callback function itself. 95*2810ac1bSKiyoung Kim// 96*2810ac1bSKiyoung Kim// If the specified callback fn should call any "cap" package 97*2810ac1bSKiyoung Kim// functions that change privilege state, these calls will only affect 98*2810ac1bSKiyoung Kim// the launch goroutine itself. While the launch is in progress, other 99*2810ac1bSKiyoung Kim// (non-launch) goroutines will block if they attempt to change 100*2810ac1bSKiyoung Kim// privilege state. These routines will unblock once there are no 101*2810ac1bSKiyoung Kim// in-flight launches. 102*2810ac1bSKiyoung Kim// 103*2810ac1bSKiyoung Kim// Note, the first argument provided to the callback function is the 104*2810ac1bSKiyoung Kim// *syscall.ProcAttr value to be used when a process launch is taking 105*2810ac1bSKiyoung Kim// place. A non-nil structure pointer can be modified by the callback 106*2810ac1bSKiyoung Kim// to enhance the launch. For example, the .Files field can be 107*2810ac1bSKiyoung Kim// overridden to affect how the launched process' stdin/out/err are 108*2810ac1bSKiyoung Kim// handled. 109*2810ac1bSKiyoung Kim// 110*2810ac1bSKiyoung Kim// Further, the 2nd argument to the callback function is provided at 111*2810ac1bSKiyoung Kim// Launch() invocation and can communicate contextual info to and from 112*2810ac1bSKiyoung Kim// the callback and the main process. 113*2810ac1bSKiyoung Kimfunc (attr *Launcher) Callback(fn func(*syscall.ProcAttr, interface{}) error) { 114*2810ac1bSKiyoung Kim if attr == nil { 115*2810ac1bSKiyoung Kim return 116*2810ac1bSKiyoung Kim } 117*2810ac1bSKiyoung Kim attr.mu.Lock() 118*2810ac1bSKiyoung Kim defer attr.mu.Unlock() 119*2810ac1bSKiyoung Kim attr.callbackFn = fn 120*2810ac1bSKiyoung Kim} 121*2810ac1bSKiyoung Kim 122*2810ac1bSKiyoung Kim// SetUID specifies the UID to be used by the launched command. 123*2810ac1bSKiyoung Kimfunc (attr *Launcher) SetUID(uid int) { 124*2810ac1bSKiyoung Kim if attr == nil { 125*2810ac1bSKiyoung Kim return 126*2810ac1bSKiyoung Kim } 127*2810ac1bSKiyoung Kim attr.mu.Lock() 128*2810ac1bSKiyoung Kim defer attr.mu.Unlock() 129*2810ac1bSKiyoung Kim attr.changeUIDs = true 130*2810ac1bSKiyoung Kim attr.uid = uid 131*2810ac1bSKiyoung Kim} 132*2810ac1bSKiyoung Kim 133*2810ac1bSKiyoung Kim// SetGroups specifies the GID and supplementary groups for the 134*2810ac1bSKiyoung Kim// launched command. 135*2810ac1bSKiyoung Kimfunc (attr *Launcher) SetGroups(gid int, groups []int) { 136*2810ac1bSKiyoung Kim if attr == nil { 137*2810ac1bSKiyoung Kim return 138*2810ac1bSKiyoung Kim } 139*2810ac1bSKiyoung Kim attr.mu.Lock() 140*2810ac1bSKiyoung Kim defer attr.mu.Unlock() 141*2810ac1bSKiyoung Kim attr.changeGIDs = true 142*2810ac1bSKiyoung Kim attr.gid = gid 143*2810ac1bSKiyoung Kim attr.groups = groups 144*2810ac1bSKiyoung Kim} 145*2810ac1bSKiyoung Kim 146*2810ac1bSKiyoung Kim// SetMode specifies the libcap Mode to be used by the launched command. 147*2810ac1bSKiyoung Kimfunc (attr *Launcher) SetMode(mode Mode) { 148*2810ac1bSKiyoung Kim if attr == nil { 149*2810ac1bSKiyoung Kim return 150*2810ac1bSKiyoung Kim } 151*2810ac1bSKiyoung Kim attr.mu.Lock() 152*2810ac1bSKiyoung Kim defer attr.mu.Unlock() 153*2810ac1bSKiyoung Kim attr.changeMode = true 154*2810ac1bSKiyoung Kim attr.mode = mode 155*2810ac1bSKiyoung Kim} 156*2810ac1bSKiyoung Kim 157*2810ac1bSKiyoung Kim// SetIAB specifies the IAB capability vectors to be inherited by the 158*2810ac1bSKiyoung Kim// launched command. A nil value means the prevailing vectors of the 159*2810ac1bSKiyoung Kim// parent will be inherited. Note, a duplicate of the provided IAB 160*2810ac1bSKiyoung Kim// tuple is actually stored, so concurrent modification of the iab 161*2810ac1bSKiyoung Kim// value does not affect the launcher. 162*2810ac1bSKiyoung Kimfunc (attr *Launcher) SetIAB(iab *IAB) { 163*2810ac1bSKiyoung Kim if attr == nil { 164*2810ac1bSKiyoung Kim return 165*2810ac1bSKiyoung Kim } 166*2810ac1bSKiyoung Kim attr.mu.Lock() 167*2810ac1bSKiyoung Kim defer attr.mu.Unlock() 168*2810ac1bSKiyoung Kim attr.iab, _ = iab.Dup() 169*2810ac1bSKiyoung Kim} 170*2810ac1bSKiyoung Kim 171*2810ac1bSKiyoung Kim// SetChroot specifies the chroot value to be used by the launched 172*2810ac1bSKiyoung Kim// command. An empty value means no-change from the prevailing value. 173*2810ac1bSKiyoung Kimfunc (attr *Launcher) SetChroot(root string) { 174*2810ac1bSKiyoung Kim if attr == nil { 175*2810ac1bSKiyoung Kim return 176*2810ac1bSKiyoung Kim } 177*2810ac1bSKiyoung Kim attr.mu.Lock() 178*2810ac1bSKiyoung Kim defer attr.mu.Unlock() 179*2810ac1bSKiyoung Kim attr.chroot = root 180*2810ac1bSKiyoung Kim} 181*2810ac1bSKiyoung Kim 182*2810ac1bSKiyoung Kim// lResult is used to get the result from the doomed launcher thread. 183*2810ac1bSKiyoung Kimtype lResult struct { 184*2810ac1bSKiyoung Kim // tgid holds the thread group id, which is an alias for the 185*2810ac1bSKiyoung Kim // shared process id of the parent program. 186*2810ac1bSKiyoung Kim tgid int 187*2810ac1bSKiyoung Kim 188*2810ac1bSKiyoung Kim // tid holds the tid of the locked launching thread which dies 189*2810ac1bSKiyoung Kim // as the launch completes. 190*2810ac1bSKiyoung Kim tid int 191*2810ac1bSKiyoung Kim 192*2810ac1bSKiyoung Kim // pid is the pid of the launched program (path, args). In 193*2810ac1bSKiyoung Kim // the case of a FuncLaunch() this value is zero on success. 194*2810ac1bSKiyoung Kim // pid holds -1 in the case of error. 195*2810ac1bSKiyoung Kim pid int 196*2810ac1bSKiyoung Kim 197*2810ac1bSKiyoung Kim // err is nil on success, but otherwise holds the reason the 198*2810ac1bSKiyoung Kim // launch failed. 199*2810ac1bSKiyoung Kim err error 200*2810ac1bSKiyoung Kim} 201*2810ac1bSKiyoung Kim 202*2810ac1bSKiyoung Kim// ErrLaunchFailed is returned if a launch was aborted with no more 203*2810ac1bSKiyoung Kim// specific error. 204*2810ac1bSKiyoung Kimvar ErrLaunchFailed = errors.New("launch failed") 205*2810ac1bSKiyoung Kim 206*2810ac1bSKiyoung Kim// ErrNoLaunch indicates the go runtime available to this binary does 207*2810ac1bSKiyoung Kim// not reliably support launching. See cap.LaunchSupported. 208*2810ac1bSKiyoung Kimvar ErrNoLaunch = errors.New("launch not supported") 209*2810ac1bSKiyoung Kim 210*2810ac1bSKiyoung Kim// ErrAmbiguousChroot indicates that the Launcher is being used in 211*2810ac1bSKiyoung Kim// addition to a callback supplied Chroot. The former should be used 212*2810ac1bSKiyoung Kim// exclusively for this. 213*2810ac1bSKiyoung Kimvar ErrAmbiguousChroot = errors.New("use Launcher for chroot") 214*2810ac1bSKiyoung Kim 215*2810ac1bSKiyoung Kim// ErrAmbiguousIDs indicates that the Launcher is being used in 216*2810ac1bSKiyoung Kim// addition to a callback supplied Credentials. The former should be 217*2810ac1bSKiyoung Kim// used exclusively for this. 218*2810ac1bSKiyoung Kimvar ErrAmbiguousIDs = errors.New("use Launcher for uids and gids") 219*2810ac1bSKiyoung Kim 220*2810ac1bSKiyoung Kim// ErrAmbiguousAmbient indicates that the Launcher is being used in 221*2810ac1bSKiyoung Kim// addition to a callback supplied ambient set and the former should 222*2810ac1bSKiyoung Kim// be used exclusively in a Launch call. 223*2810ac1bSKiyoung Kimvar ErrAmbiguousAmbient = errors.New("use Launcher for ambient caps") 224*2810ac1bSKiyoung Kim 225*2810ac1bSKiyoung Kim// lName is the name we temporarily give to the launcher thread. Note, 226*2810ac1bSKiyoung Kim// this will likely stick around in the process tree if the Go runtime 227*2810ac1bSKiyoung Kim// is not cleaning up locked launcher OS threads. 228*2810ac1bSKiyoung Kimvar lName = []byte("cap-launcher\000") 229*2810ac1bSKiyoung Kim 230*2810ac1bSKiyoung Kim// <uapi/linux/prctl.h> 231*2810ac1bSKiyoung Kimconst prSetName = 15 232*2810ac1bSKiyoung Kim 233*2810ac1bSKiyoung Kim//go:uintptrescapes 234*2810ac1bSKiyoung Kimfunc launch(result chan<- lResult, attr *Launcher, data interface{}, quit chan<- struct{}) { 235*2810ac1bSKiyoung Kim if quit != nil { 236*2810ac1bSKiyoung Kim defer close(quit) 237*2810ac1bSKiyoung Kim } 238*2810ac1bSKiyoung Kim 239*2810ac1bSKiyoung Kim // Thread group ID is the process ID. 240*2810ac1bSKiyoung Kim tgid := syscall.Getpid() 241*2810ac1bSKiyoung Kim 242*2810ac1bSKiyoung Kim // This code waits until we are not scheduled on the parent 243*2810ac1bSKiyoung Kim // thread. We will exit this thread once the child has 244*2810ac1bSKiyoung Kim // launched. 245*2810ac1bSKiyoung Kim runtime.LockOSThread() 246*2810ac1bSKiyoung Kim tid := syscall.Gettid() 247*2810ac1bSKiyoung Kim if tid == tgid { 248*2810ac1bSKiyoung Kim // Force the go runtime to find a new thread to run 249*2810ac1bSKiyoung Kim // on. (It is really awkward to have a process' 250*2810ac1bSKiyoung Kim // PID=TID thread in effectively a zombie state. The 251*2810ac1bSKiyoung Kim // Go runtime has support for it, but pstree gives 252*2810ac1bSKiyoung Kim // ugly output since the prSetName value sticks around 253*2810ac1bSKiyoung Kim // after launch completion... 254*2810ac1bSKiyoung Kim // 255*2810ac1bSKiyoung Kim // (Optimize for time to debug by reducing ugly spam 256*2810ac1bSKiyoung Kim // like this.) 257*2810ac1bSKiyoung Kim quit := make(chan struct{}) 258*2810ac1bSKiyoung Kim go launch(result, attr, data, quit) 259*2810ac1bSKiyoung Kim 260*2810ac1bSKiyoung Kim // Wait for that go routine to complete. 261*2810ac1bSKiyoung Kim <-quit 262*2810ac1bSKiyoung Kim runtime.UnlockOSThread() 263*2810ac1bSKiyoung Kim return 264*2810ac1bSKiyoung Kim } 265*2810ac1bSKiyoung Kim 266*2810ac1bSKiyoung Kim // Provide a way to serialize the caller on the thread 267*2810ac1bSKiyoung Kim // completing. This should be done by the one locked tid that 268*2810ac1bSKiyoung Kim // does the ForkExec(). All the other threads have a different 269*2810ac1bSKiyoung Kim // security context. 270*2810ac1bSKiyoung Kim defer close(result) 271*2810ac1bSKiyoung Kim 272*2810ac1bSKiyoung Kim // By never releasing the LockOSThread here, we guarantee that 273*2810ac1bSKiyoung Kim // the runtime will terminate the current OS thread once this 274*2810ac1bSKiyoung Kim // function returns. 275*2810ac1bSKiyoung Kim scwSetState(launchIdle, launchActive, tid) 276*2810ac1bSKiyoung Kim 277*2810ac1bSKiyoung Kim // Name the launcher thread - transient, but helps to debug if 278*2810ac1bSKiyoung Kim // the callbackFn or something else hangs up. 279*2810ac1bSKiyoung Kim singlesc.prctlrcall(prSetName, uintptr(unsafe.Pointer(&lName[0])), 0) 280*2810ac1bSKiyoung Kim 281*2810ac1bSKiyoung Kim var pa *syscall.ProcAttr 282*2810ac1bSKiyoung Kim var err error 283*2810ac1bSKiyoung Kim var needChroot bool 284*2810ac1bSKiyoung Kim 285*2810ac1bSKiyoung Kim // Only prepare a non-nil pa value if a path is provided. 286*2810ac1bSKiyoung Kim if attr.path != "" { 287*2810ac1bSKiyoung Kim // By default the following file descriptors are preserved for 288*2810ac1bSKiyoung Kim // the child. The user should modify them in the callback for 289*2810ac1bSKiyoung Kim // stdin/out/err redirection. 290*2810ac1bSKiyoung Kim pa = &syscall.ProcAttr{ 291*2810ac1bSKiyoung Kim Files: []uintptr{0, 1, 2}, 292*2810ac1bSKiyoung Kim } 293*2810ac1bSKiyoung Kim if len(attr.env) != 0 { 294*2810ac1bSKiyoung Kim pa.Env = attr.env 295*2810ac1bSKiyoung Kim } else { 296*2810ac1bSKiyoung Kim pa.Env = os.Environ() 297*2810ac1bSKiyoung Kim } 298*2810ac1bSKiyoung Kim } 299*2810ac1bSKiyoung Kim 300*2810ac1bSKiyoung Kim var pid int 301*2810ac1bSKiyoung Kim if attr.callbackFn != nil { 302*2810ac1bSKiyoung Kim if err = attr.callbackFn(pa, data); err != nil { 303*2810ac1bSKiyoung Kim goto abort 304*2810ac1bSKiyoung Kim } 305*2810ac1bSKiyoung Kim if attr.path == "" { 306*2810ac1bSKiyoung Kim goto abort 307*2810ac1bSKiyoung Kim } 308*2810ac1bSKiyoung Kim } 309*2810ac1bSKiyoung Kim 310*2810ac1bSKiyoung Kim if needChroot, err = validatePA(pa, attr.chroot); err != nil { 311*2810ac1bSKiyoung Kim goto abort 312*2810ac1bSKiyoung Kim } 313*2810ac1bSKiyoung Kim if attr.changeUIDs { 314*2810ac1bSKiyoung Kim if err = singlesc.setUID(attr.uid); err != nil { 315*2810ac1bSKiyoung Kim goto abort 316*2810ac1bSKiyoung Kim } 317*2810ac1bSKiyoung Kim } 318*2810ac1bSKiyoung Kim if attr.changeGIDs { 319*2810ac1bSKiyoung Kim if err = singlesc.setGroups(attr.gid, attr.groups); err != nil { 320*2810ac1bSKiyoung Kim goto abort 321*2810ac1bSKiyoung Kim } 322*2810ac1bSKiyoung Kim } 323*2810ac1bSKiyoung Kim if attr.changeMode { 324*2810ac1bSKiyoung Kim if err = singlesc.setMode(attr.mode); err != nil { 325*2810ac1bSKiyoung Kim goto abort 326*2810ac1bSKiyoung Kim } 327*2810ac1bSKiyoung Kim } 328*2810ac1bSKiyoung Kim if attr.iab != nil { 329*2810ac1bSKiyoung Kim // Note, since .iab is a private copy we don't need to 330*2810ac1bSKiyoung Kim // lock it around this call. 331*2810ac1bSKiyoung Kim if err = singlesc.iabSetProc(attr.iab); err != nil { 332*2810ac1bSKiyoung Kim goto abort 333*2810ac1bSKiyoung Kim } 334*2810ac1bSKiyoung Kim } 335*2810ac1bSKiyoung Kim 336*2810ac1bSKiyoung Kim if needChroot { 337*2810ac1bSKiyoung Kim c := GetProc() 338*2810ac1bSKiyoung Kim if err = c.SetFlag(Effective, true, SYS_CHROOT); err != nil { 339*2810ac1bSKiyoung Kim goto abort 340*2810ac1bSKiyoung Kim } 341*2810ac1bSKiyoung Kim if err = singlesc.setProc(c); err != nil { 342*2810ac1bSKiyoung Kim goto abort 343*2810ac1bSKiyoung Kim } 344*2810ac1bSKiyoung Kim } 345*2810ac1bSKiyoung Kim pid, err = syscall.ForkExec(attr.path, attr.args, pa) 346*2810ac1bSKiyoung Kim 347*2810ac1bSKiyoung Kimabort: 348*2810ac1bSKiyoung Kim if err != nil { 349*2810ac1bSKiyoung Kim pid = -1 350*2810ac1bSKiyoung Kim } 351*2810ac1bSKiyoung Kim result <- lResult{ 352*2810ac1bSKiyoung Kim tgid: tgid, 353*2810ac1bSKiyoung Kim tid: tid, 354*2810ac1bSKiyoung Kim pid: pid, 355*2810ac1bSKiyoung Kim err: err, 356*2810ac1bSKiyoung Kim } 357*2810ac1bSKiyoung Kim} 358*2810ac1bSKiyoung Kim 359*2810ac1bSKiyoung Kim// pollForThreadExit waits for a thread to terminate. Only after the 360*2810ac1bSKiyoung Kim// thread has safely exited is it safe to resume POSIX semantics 361*2810ac1bSKiyoung Kim// security state mirroring for the rest of the process threads. 362*2810ac1bSKiyoung Kimfunc (v lResult) pollForThreadExit() { 363*2810ac1bSKiyoung Kim if v.tid == -1 { 364*2810ac1bSKiyoung Kim return 365*2810ac1bSKiyoung Kim } 366*2810ac1bSKiyoung Kim for syscall.Tgkill(v.tgid, v.tid, 0) == nil { 367*2810ac1bSKiyoung Kim runtime.Gosched() 368*2810ac1bSKiyoung Kim } 369*2810ac1bSKiyoung Kim scwSetState(launchActive, launchIdle, v.tid) 370*2810ac1bSKiyoung Kim} 371*2810ac1bSKiyoung Kim 372*2810ac1bSKiyoung Kim// Launch performs a callback function and/or new program launch with 373*2810ac1bSKiyoung Kim// a disposable security state. The data object, when not nil, can be 374*2810ac1bSKiyoung Kim// used to communicate with the callback. It can also be used to 375*2810ac1bSKiyoung Kim// return details from the callback function's execution. 376*2810ac1bSKiyoung Kim// 377*2810ac1bSKiyoung Kim// If the attr was created with NewLauncher(), this present function 378*2810ac1bSKiyoung Kim// will return the pid of the launched process, or -1 and a non-nil 379*2810ac1bSKiyoung Kim// error. 380*2810ac1bSKiyoung Kim// 381*2810ac1bSKiyoung Kim// If the attr was created with FuncLauncher(), this present function 382*2810ac1bSKiyoung Kim// will return 0, nil if the callback function exits without 383*2810ac1bSKiyoung Kim// error. Otherwise it will return -1 and the non-nil error of the 384*2810ac1bSKiyoung Kim// callback return value. 385*2810ac1bSKiyoung Kim// 386*2810ac1bSKiyoung Kim// Note, while the disposable security state thread makes some 387*2810ac1bSKiyoung Kim// operations seem more isolated - they are *not securely 388*2810ac1bSKiyoung Kim// isolated*. Launching is inherently violating the POSIX semantics 389*2810ac1bSKiyoung Kim// maintained by the rest of the "libcap/cap" package, so think of 390*2810ac1bSKiyoung Kim// launching as a convenience wrapper around fork()ing. 391*2810ac1bSKiyoung Kim// 392*2810ac1bSKiyoung Kim// Advanced user note: if the caller of this function thinks they know 393*2810ac1bSKiyoung Kim// what they are doing by using runtime.LockOSThread() before invoking 394*2810ac1bSKiyoung Kim// this function, they should understand that the OS thread invoking 395*2810ac1bSKiyoung Kim// (*Launcher).Launch() is *not* guaranteed to be the one used for the 396*2810ac1bSKiyoung Kim// disposable security state to perform the launch. If said caller 397*2810ac1bSKiyoung Kim// needs to run something on the disposable security state thread, 398*2810ac1bSKiyoung Kim// they should do it via the launch callback function mechanism. (The 399*2810ac1bSKiyoung Kim// Go runtime is complicated and this is why this Launch mechanism 400*2810ac1bSKiyoung Kim// provides the optional callback function.) 401*2810ac1bSKiyoung Kimfunc (attr *Launcher) Launch(data interface{}) (int, error) { 402*2810ac1bSKiyoung Kim if !LaunchSupported { 403*2810ac1bSKiyoung Kim return -1, ErrNoLaunch 404*2810ac1bSKiyoung Kim } 405*2810ac1bSKiyoung Kim if attr == nil { 406*2810ac1bSKiyoung Kim return -1, ErrLaunchFailed 407*2810ac1bSKiyoung Kim } 408*2810ac1bSKiyoung Kim attr.mu.RLock() 409*2810ac1bSKiyoung Kim defer attr.mu.RUnlock() 410*2810ac1bSKiyoung Kim if attr.callbackFn == nil && (attr.path == "" || len(attr.args) == 0) { 411*2810ac1bSKiyoung Kim return -1, ErrLaunchFailed 412*2810ac1bSKiyoung Kim } 413*2810ac1bSKiyoung Kim 414*2810ac1bSKiyoung Kim result := make(chan lResult) 415*2810ac1bSKiyoung Kim go launch(result, attr, data, nil) 416*2810ac1bSKiyoung Kim v, ok := <-result 417*2810ac1bSKiyoung Kim if !ok { 418*2810ac1bSKiyoung Kim return -1, ErrLaunchFailed 419*2810ac1bSKiyoung Kim } 420*2810ac1bSKiyoung Kim <-result // blocks until the launch() goroutine exits 421*2810ac1bSKiyoung Kim v.pollForThreadExit() 422*2810ac1bSKiyoung Kim return v.pid, v.err 423*2810ac1bSKiyoung Kim} 424