// UnpackWithDropPrivs will unapck the ClickDeb content into the // target dir and drop privs when doing this. // // To do this reliably in go we need to exec a helper as we can not // just fork() and drop privs in the child (no support for stock fork in go) func (d *ClickDeb) UnpackWithDropPrivs(instDir, rootdir string) error { // no need to drop privs, we are not root if !helpers.ShouldDropPrivs() { return d.Unpack(instDir) } cmd := exec.Command("snappy", "internal-unpack", d.Name(), instDir, rootdir) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return &ErrUnpackFailed{ snapFile: d.Name(), instDir: instDir, origErr: err, } } return nil }
func unpackAndDropPrivs(snapFile, targetDir, rootDir string) error { d, err := clickdeb.Open(snapFile) if err != nil { return err } defer d.Close() if helpers.ShouldDropPrivs() { var dropPrivsUser string // first find out what user to use passFile := passwdFile(rootDir, "passwd") for _, dropPrivsUser = range dropPrivsUsers { _, err := readUID(dropPrivsUser, passFile) if err == nil { break } } // then get uid/gid uid, err := readUID(dropPrivsUser, passFile) if err != nil { return err } groupFile := passwdFile(rootDir, "group") gid, err := readUID(dropPrivsUser, groupFile) if err != nil { return err } if err := os.MkdirAll(targetDir, 0755); err != nil { return err } if err := os.Chown(targetDir, uid, gid); err != nil { return err } // Setuid and Setgid only apply to the current Linux thread, so make // sure we don't get moved. runtime.LockOSThread() // run prctl(PR_SET_NO_NEW_PRIVS) rc := C.prctl_no_new_privs() if rc < 0 { return fmt.Errorf("prctl(PR_SET_NO_NEW_PRIVS) failed with %v", rc) } if err := syscall.Setgroups([]int{gid}); err != nil { return fmt.Errorf("Setgroups([]{%d}) call failed: %v", gid, err) } if err := setgid(gid); err != nil { return fmt.Errorf("Setgid(%d) call failed: %v", gid, err) } if err := setuid(uid); err != nil { return fmt.Errorf("Setuid(%d) call failed: %v", uid, err) } // extra paranoia if syscall.Getuid() != uid || syscall.Getgid() != gid { return fmt.Errorf("Dropping privileges failed, uid is %v, gid is %v", syscall.Getuid(), syscall.Getgid()) } } return d.UnpackAll(targetDir) }