mirror of https://github.com/xSmurf/oz.git
				
				
				
			Oz setup utility, handling of launching from diverted binary, handling of launching in running sandbox
	
		
	
				
					
				
			
							parent
							
								
									00d1aabc25
								
							
						
					
					
						commit
						8ff505f2e3
					
				@ -0,0 +1,311 @@
 | 
				
			|||||||
 | 
					package main
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import (
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
						"os"
 | 
				
			||||||
 | 
						"os/exec"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/subgraph/oz"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/codegangsta/cli"
 | 
				
			||||||
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					var PathDpkgDivert string
 | 
				
			||||||
 | 
					var PathDpkg string
 | 
				
			||||||
 | 
					var OzConfig *oz.Config
 | 
				
			||||||
 | 
					var OzProfiles *oz.Profiles
 | 
				
			||||||
 | 
					var OzProfile *oz.Profile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						checkRoot()
 | 
				
			||||||
 | 
						PathDpkgDivert = checkDpkgDivert()
 | 
				
			||||||
 | 
						PathDpkg = checkDpkg()
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func main() {
 | 
				
			||||||
 | 
						app := cli.NewApp()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						app.Name = "oz-utils"
 | 
				
			||||||
 | 
						app.Usage = "command line interface to install, remove, and create Oz sandboxes\nYou can specify a package name, a binary path, or a Oz profile file "
 | 
				
			||||||
 | 
						app.Author = "Subgraph"
 | 
				
			||||||
 | 
						app.Email = "info@subgraph.com"
 | 
				
			||||||
 | 
						app.Version = "0.0.1"
 | 
				
			||||||
 | 
						app.EnableBashCompletion = true
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						flagsHookMode := []cli.Flag{
 | 
				
			||||||
 | 
							cli.BoolFlag{
 | 
				
			||||||
 | 
								Name:  "hook",
 | 
				
			||||||
 | 
								Usage: "Run in hook mode, not normally used by the end user",
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						app.Commands = []cli.Command{
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name:  "config",
 | 
				
			||||||
 | 
								Usage: "check and show Oz configurations",
 | 
				
			||||||
 | 
								Subcommands: []cli.Command{
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Name:   "check",
 | 
				
			||||||
 | 
										Usage:  "check oz configuration and profiles for errors",
 | 
				
			||||||
 | 
										Action: handleConfigcheck,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
									{
 | 
				
			||||||
 | 
										Name:   "show",
 | 
				
			||||||
 | 
										Usage:  "prints ouf oz configuration",
 | 
				
			||||||
 | 
										Action: handleConfigshow,
 | 
				
			||||||
 | 
									},
 | 
				
			||||||
 | 
								},
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name:   "install",
 | 
				
			||||||
 | 
								Usage:  "install binary diversion for a program",
 | 
				
			||||||
 | 
								Action: handleInstall,
 | 
				
			||||||
 | 
								Flags:  flagsHookMode,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name:   "remove",
 | 
				
			||||||
 | 
								Usage:  "remove a binary diversion for a program",
 | 
				
			||||||
 | 
								Action: handleRemove,
 | 
				
			||||||
 | 
								Flags:  flagsHookMode,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name:   "status",
 | 
				
			||||||
 | 
								Usage:  "show the status of a binary diversion for a program",
 | 
				
			||||||
 | 
								Action: handleStatus,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
							{
 | 
				
			||||||
 | 
								Name:   "create",
 | 
				
			||||||
 | 
								Usage:  "create a new sandbox profile",
 | 
				
			||||||
 | 
								Action: handleCreate,
 | 
				
			||||||
 | 
							},
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						app.Run(os.Args)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleConfigcheck(c *cli.Context) {
 | 
				
			||||||
 | 
						fmt.Println("Here be dragons!")
 | 
				
			||||||
 | 
						os.Exit(1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleConfigshow(c *cli.Context) {
 | 
				
			||||||
 | 
						handleConfigcheck(c)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleInstall(c *cli.Context) {
 | 
				
			||||||
 | 
						OzConfig = loadConfig()
 | 
				
			||||||
 | 
						pname := c.Args()[0]
 | 
				
			||||||
 | 
						OzProfile, err := loadProfile(pname, OzConfig.ProfileDir)
 | 
				
			||||||
 | 
						if err != nil || OzProfile == nil {
 | 
				
			||||||
 | 
							installExit(c.Bool("hook"), fmt.Errorf("Unable to load profiles for %s.\n", pname))
 | 
				
			||||||
 | 
							return // For clarity
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if OzConfig.DivertSuffix == "" {
 | 
				
			||||||
 | 
							installExit(c.Bool("hook"), fmt.Errorf("Divert requires a suffix to be set.\n"))
 | 
				
			||||||
 | 
							return // For clarity
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isInstalled, err := isDivertInstalled(OzProfile.Path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if isInstalled == true {
 | 
				
			||||||
 | 
							fmt.Println("Divert already installed for ", OzProfile.Path)
 | 
				
			||||||
 | 
							os.Exit(0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dpkgArgs := []string{
 | 
				
			||||||
 | 
							"--add",
 | 
				
			||||||
 | 
							"--package",
 | 
				
			||||||
 | 
							"oz",
 | 
				
			||||||
 | 
							"--rename",
 | 
				
			||||||
 | 
							"--divert",
 | 
				
			||||||
 | 
							getBinaryPath(OzProfile.Path),
 | 
				
			||||||
 | 
							OzProfile.Path,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = exec.Command(PathDpkgDivert, dpkgArgs...).Output()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Dpkg divert command `%s %+s` failed: %s", PathDpkgDivert, dpkgArgs, err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						err = syscall.Symlink(OzConfig.ClientPath, OzProfile.Path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Failed to create symlink %s", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("Successfully installed Oz sandbox for: %s.\n", OzProfile.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleRemove(c *cli.Context) {
 | 
				
			||||||
 | 
						OzConfig = loadConfig()
 | 
				
			||||||
 | 
						pname := c.Args()[0]
 | 
				
			||||||
 | 
						OzProfile, err := loadProfile(pname, OzConfig.ProfileDir)
 | 
				
			||||||
 | 
						if err != nil || OzProfile == nil {
 | 
				
			||||||
 | 
							installExit(c.Bool("hook"), fmt.Errorf("Unable to load profiles for %s.\n", pname))
 | 
				
			||||||
 | 
							return // For clarity
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if OzConfig.DivertSuffix == "" {
 | 
				
			||||||
 | 
							installExit(c.Bool("hook"), fmt.Errorf("Divert requires a suffix to be set.\n"))
 | 
				
			||||||
 | 
							return // For clarity
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isInstalled, err := isDivertInstalled(OzProfile.Path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if isInstalled == false {
 | 
				
			||||||
 | 
							fmt.Println("Divert is not installed for ", OzProfile.Path)
 | 
				
			||||||
 | 
							os.Exit(0)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						os.Remove(OzProfile.Path)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dpkgArgs := []string{
 | 
				
			||||||
 | 
							"--rename",
 | 
				
			||||||
 | 
							"--package",
 | 
				
			||||||
 | 
							"oz",
 | 
				
			||||||
 | 
							"--remove",
 | 
				
			||||||
 | 
							OzProfile.Path,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						_, err = exec.Command(PathDpkgDivert, dpkgArgs...).Output()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Dpkg divert command `%s %+s` failed: %s", PathDpkgDivert, dpkgArgs, err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Printf("Successfully remove jail for: %s.\n", OzProfile.Path)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleStatus(c *cli.Context) {
 | 
				
			||||||
 | 
						OzConfig = loadConfig()
 | 
				
			||||||
 | 
						pname := c.Args()[0]
 | 
				
			||||||
 | 
						OzProfile, err := loadProfile(pname, OzConfig.ProfileDir)
 | 
				
			||||||
 | 
						if err != nil || OzProfile == nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Unable to load profiles (%s).\n", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if OzConfig.DivertSuffix == "" {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Divert requires a suffix to be set.\n")
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isInstalled, err := isDivertInstalled(OzProfile.Path)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "Unknown error: %+v\n", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						if isInstalled {
 | 
				
			||||||
 | 
							fmt.Println("Package divert is \033[0;32minstalled\033[0m for: ", OzProfile.Path)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fmt.Println("Package divert is \033[0;31mnot installed\033[0m for: ", OzProfile.Path)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func handleCreate(c *cli.Context) {
 | 
				
			||||||
 | 
						OzConfig = loadConfig()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fmt.Println("The weasels ran off with this command... please come back later!")
 | 
				
			||||||
 | 
						os.Exit(1)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					* UTILITIES
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkRoot() {
 | 
				
			||||||
 | 
						if os.Getuid() != 0 {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "%s should be used as root.\n", os.Args[0])
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkDpkgDivert() string {
 | 
				
			||||||
 | 
						ddpath, err := exec.LookPath("dpkg-divert")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintln(os.Stderr, "You do not appear to have dpkg-divert, are you not running Debian/Ubuntu?")
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return ddpath
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func checkDpkg() string {
 | 
				
			||||||
 | 
						dpath, err := exec.LookPath("dpkg")
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							fmt.Fprintln(os.Stderr, "You do not appear to have dpkg, are you not running Debian/Ubuntu?")
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return dpath
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func isDivertInstalled(bpath string) (bool, error) {
 | 
				
			||||||
 | 
						outp, err := exec.Command(PathDpkgDivert, "--truename", bpath).Output()
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return false, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						dpath := strings.TrimSpace(string(outp))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						isInstalled := (dpath == getBinaryPath(string(bpath)))
 | 
				
			||||||
 | 
						if isInstalled {
 | 
				
			||||||
 | 
							_, err := os.Readlink(bpath)
 | 
				
			||||||
 | 
							if err != nil {
 | 
				
			||||||
 | 
								return false, fmt.Errorf("`%s` appears to be diverted but is not installed", dpath)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return isInstalled, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func getBinaryPath(bpath string) string {
 | 
				
			||||||
 | 
						bpath = strings.TrimSpace(string(bpath))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if strings.HasSuffix(bpath, "."+OzConfig.DivertSuffix) == false {
 | 
				
			||||||
 | 
							bpath += "." + OzConfig.DivertSuffix
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return bpath
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func loadConfig() *oz.Config {
 | 
				
			||||||
 | 
						config, err := oz.LoadConfig(oz.DefaultConfigPath)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							if os.IsNotExist(err) {
 | 
				
			||||||
 | 
								fmt.Fprintln(os.Stderr, "Configuration file (%s) is missing, using defaults.", oz.DefaultConfigPath)
 | 
				
			||||||
 | 
								config = oz.NewDefaultConfig()
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								fmt.Fprintln(os.Stderr, "Could not load configuration: %s", oz.DefaultConfigPath, err)
 | 
				
			||||||
 | 
								os.Exit(1)
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return config
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func loadProfile(name, profileDir string) (*oz.Profile, error) {
 | 
				
			||||||
 | 
						ps, err := oz.LoadProfiles(profileDir)
 | 
				
			||||||
 | 
						if err != nil {
 | 
				
			||||||
 | 
							return nil, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return ps.GetProfileByName(name)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func installExit(hook bool, err error) {
 | 
				
			||||||
 | 
						if hook {
 | 
				
			||||||
 | 
							os.Exit(0)
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							fmt.Fprintf(os.Stderr, "%s", err)
 | 
				
			||||||
 | 
							os.Exit(1)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
					Loading…
					
					
				
		Reference in new issue