diff options
| author | Jakob Unterwurzacher | 2015-11-14 21:25:10 +0100 | 
|---|---|---|
| committer | Jakob Unterwurzacher | 2015-11-14 21:25:10 +0100 | 
| commit | 58592330dc33e194e55fa942d3f375124839f058 (patch) | |
| tree | 7b523afe12b7382556ca591768e05ed397b0512f | |
| parent | 6736212b29bd54a0ed9778f1bbea49db03f43c06 (diff) | |
Refactor cli argument handling
Also, add the "-config" option for storing gocryptfs.conf
outside of CIPHERDIR.
| -rw-r--r-- | MANPAGE.md | 2 | ||||
| -rw-r--r-- | checkdir.go | 2 | ||||
| -rw-r--r-- | daemonize.go | 15 | ||||
| -rw-r--r-- | main.go | 245 | ||||
| -rw-r--r-- | masterkey.go | 3 | 
5 files changed, 153 insertions, 114 deletions
@@ -23,7 +23,7 @@ gocryptfs [OPTIONS] CIPHERDIR MOUNTPOINT  Change password  --------------- -gocryptfs -passwd CIPHERDIR +gocryptfs -passwd [OPTIONS] CIPHERDIR  DESCRIPTION  =========== diff --git a/checkdir.go b/checkdir.go index 9614d7d..6f7f75d 100644 --- a/checkdir.go +++ b/checkdir.go @@ -6,6 +6,7 @@ import (  	"os"  ) +// checkDirEmpty - check if "dir" exists and is an empty directory  func checkDirEmpty(dir string) error {  	err := checkDir(dir)  	if err != nil { @@ -21,6 +22,7 @@ func checkDirEmpty(dir string) error {  	return fmt.Errorf("directory %s not empty", dir)  } +// checkDir - check if "dir" exists and is a directory  func checkDir(dir string) error {  	fi, err := os.Stat(dir)  	if err != nil { diff --git a/daemonize.go b/daemonize.go index 4f09164..a65bd11 100644 --- a/daemonize.go +++ b/daemonize.go @@ -9,17 +9,18 @@ import (  )  // The child sends us USR1 if the mount was successful -func waitForUsr1() { +func exitOnUsr1() {  	c := make(chan os.Signal, 1)  	signal.Notify(c, syscall.SIGUSR1)  	<-c  	os.Exit(0)  } -// daemonize - execute ourselves once again, this time with the "-f" flag, and -// wait for SIGUSR1. -func daemonize() { -	go waitForUsr1() +// forkChild - execute ourselves once again, this time with the "-f" flag, and +// wait for SIGUSR1 or child exit. +// This is a workaround for the missing true fork function in Go. +func forkChild() { +	go exitOnUsr1()  	name := os.Args[0]  	notifyArg := fmt.Sprintf("-notifypid=%d", os.Getpid())  	newArgs := []string{"-f", notifyArg} @@ -30,7 +31,7 @@ func daemonize() {  	c.Stdin = os.Stdin  	err := c.Start()  	if err != nil { -		fmt.Printf("daemonize: starting %s failed: %v\n", name, err) +		fmt.Printf("forkChild: starting %s failed: %v\n", name, err)  		os.Exit(1)  	}  	err = c.Wait() @@ -40,7 +41,7 @@ func daemonize() {  				os.Exit(waitstat.ExitStatus())  			}  		} -		fmt.Printf("daemonize: wait returned an unknown error: %v\n", err) +		fmt.Printf("forkChild: wait returned an unknown error: %v\n", err)  		os.Exit(1)  	}  	// The child exited with 0 - let's do the same. @@ -36,19 +36,16 @@ const (  // GitVersion will be set by the build script "build.bash"  var GitVersion = "[version not set - please compile using ./build.bash]" -func initDir(dirArg string, plaintextNames bool) { -	dir, _ := filepath.Abs(dirArg) - -	err := checkDirEmpty(dir) +func initDir(args *argContainer) { +	err := checkDirEmpty(args.cipherdir)  	if err != nil {  		fmt.Printf("Invalid CIPHERDIR: %v\n", err)  		os.Exit(ERREXIT_INIT)  	} -	confName := filepath.Join(dir, cryptfs.ConfDefaultName)  	cryptfs.Info.Printf("Choose a password for protecting your files.\n")  	password := readPasswordTwice() -	err = cryptfs.CreateConfFile(confName, password, plaintextNames) +	err = cryptfs.CreateConfFile(args.config, password, args.plaintextnames)  	if err != nil {  		fmt.Println(err)  		os.Exit(ERREXIT_INIT) @@ -58,23 +55,71 @@ func initDir(dirArg string, plaintextNames bool) {  }  func usageText() { -	fmt.Fprintf(os.Stderr, "Usage: %s -init [OPTIONS] CIPHERDIR\n", PROGRAM_NAME) -	fmt.Fprintf(os.Stderr, "       %s [OPTIONS] CIPHERDIR MOUNTPOINT\n", PROGRAM_NAME) -	fmt.Fprintf(os.Stderr, "\nOptions:\n") +	printVersion() +	fmt.Printf("\n") +	fmt.Printf("Usage: %s -init|-passwd [OPTIONS] CIPHERDIR\n", PROGRAM_NAME) +	fmt.Printf("  or   %s [OPTIONS] CIPHERDIR MOUNTPOINT\n", PROGRAM_NAME) +	fmt.Printf("\nOptions:\n")  	flagSet.PrintDefaults()  }  type argContainer struct {  	debug, init, zerokey, fusedebug, openssl, passwd, foreground, version,  	plaintextnames, quiet bool -	masterkey, mountpoint, cipherdir, cpuprofile string +	masterkey, mountpoint, cipherdir, cpuprofile, config string  	notifypid                                    int  }  var flagSet *flag.FlagSet +// loadConfig - load the config file "filename", prompting the user for the password +func loadConfig(filename string) (masterkey []byte, confFile *cryptfs.ConfFile) { +	// Check if the file exists at all before prompting for a password +	_, err := os.Stat(filename) +	if err != nil { +		fmt.Print(err) +		os.Exit(ERREXIT_LOADCONF) +	} +	fmt.Printf("Password: ") +	pw := readPassword() +	cryptfs.Info.Printf("Decrypting master key... ") +	cryptfs.Warn.Disable() // Silence DecryptBlock() error messages on incorrect password +	masterkey, confFile, err = cryptfs.LoadConfFile(filename, pw) +	cryptfs.Warn.Enable() +	if err != nil { +		fmt.Println(err) +		fmt.Println("Wrong password.") +		os.Exit(ERREXIT_LOADCONF) +	} +	cryptfs.Info.Printf("done.\n") + +	return masterkey, confFile +} + +// changePassword - change the password of config file "filename" +func changePassword(filename string) { +	masterkey, confFile := loadConfig(filename) +	fmt.Printf("Please enter your new password.\n") +	newPw := readPasswordTwice() +	confFile.EncryptKey(masterkey, newPw) +	err := confFile.WriteFile() +	if err != nil { +		fmt.Println(err) +		os.Exit(ERREXIT_INIT) +	} +	cryptfs.Info.Printf("Password changed.\n") +	os.Exit(0) +} + +// printVersion - print a version string like +// "gocryptfs v0.3.1-31-g6736212-dirty; on-disk format 2" +func printVersion() { +	fmt.Printf("%s %s; on-disk format %d\n", PROGRAM_NAME, GitVersion, cryptfs.HEADER_CURRENT_VERSION) +} +  func main() {  	runtime.GOMAXPROCS(4) +	var err error  	// Parse command line arguments  	var args argContainer @@ -93,20 +138,54 @@ func main() {  	flagSet.BoolVar(&args.quiet, "q", false, "Quiet - silence informational messages")  	flagSet.StringVar(&args.masterkey, "masterkey", "", "Mount with explicit master key")  	flagSet.StringVar(&args.cpuprofile, "cpuprofile", "", "Write cpu profile to specified file") +	flagSet.StringVar(&args.config, "config", "", "Use specified config file instead of CIPHERDIR/gocryptfs.conf")  	flagSet.IntVar(&args.notifypid, "notifypid", 0, "Send USR1 to the specified process after "+  		"successful mount - used internally for daemonization")  	flagSet.Parse(os.Args[1:]) +	if args.debug { +		cryptfs.Debug.Enable() +		cryptfs.Debug.Printf("Debug output enabled\n") +	} +	// By default, let the child handle everything. +	// The parent *could* handle operations that do not require backgrounding by +	// itself, but that would make the code paths more complicated. +	if !args.foreground { +		forkChild() // does not return +	} +	// Getting here means we *are* the child +	// "-v"  	if args.version { -		fmt.Printf("%s %s; on-disk format %d\n", PROGRAM_NAME, GitVersion, cryptfs.HEADER_CURRENT_VERSION) +		printVersion()  		os.Exit(0)  	} +	// Every operation below requires CIPHERDIR. Check that we have it. +	if flagSet.NArg() >= 1 { +		args.cipherdir, _ = filepath.Abs(flagSet.Arg(0)) +		err := checkDir(args.cipherdir) +		if err != nil { +			fmt.Printf("Invalid CIPHERDIR: %v\n", err) +			os.Exit(ERREXIT_CIPHERDIR) +		} +	} else { +		usageText() +		os.Exit(ERREXIT_USAGE) +	} +	// "-q"  	if args.quiet {  		cryptfs.Info.Disable()  	} -	if !args.foreground { -		daemonize() // does not return +	// "-config" +	if args.config != "" { +		args.config, err = filepath.Abs(args.config) +		if err != nil { +			fmt.Printf("Invalid \"-config\" setting: %v\n", err) +		} +		cryptfs.Info.Printf("Using config file at custom location %s\n", args.config) +	} else { +		args.config = filepath.Join(args.cipherdir, cryptfs.ConfDefaultName)  	} +	// "-cpuprofile"  	if args.cpuprofile != "" {  		f, err := os.Create(args.cpuprofile)  		if err != nil { @@ -117,123 +196,79 @@ func main() {  		pprof.StartCPUProfile(f)  		defer pprof.StopCPUProfile()  	} -	if args.debug { -		cryptfs.Debug.Enable() -		cryptfs.Debug.Printf("Debug output enabled\n") -	} +	// "-openssl"  	if args.openssl == false {  		cryptfs.Info.Printf("Openssl disabled\n")  	} +	// Operation flags: init, passwd or mount +	// "-init"  	if args.init { -		if flagSet.NArg() != 1 && args.plaintextnames == false { -			fmt.Printf("Usage: %s --init [--plaintextnames] CIPHERDIR\n", PROGRAM_NAME) +		if flagSet.NArg() > 1 { +			fmt.Printf("Usage: %s -init [OPTIONS] CIPHERDIR\n", PROGRAM_NAME)  			os.Exit(ERREXIT_USAGE)  		} -		initDir(flagSet.Arg(0), args.plaintextnames) // does not return +		initDir(&args) // does not return  	} +	// "-passwd"  	if args.passwd { -		if flagSet.NArg() != 1 { -			fmt.Printf("Usage: %s --passwd CIPHERDIR\n", PROGRAM_NAME) -			os.Exit(ERREXIT_USAGE) -		} -	} else { -		// Normal mount -		if flagSet.NArg() < 2 { -			usageText() +		if flagSet.NArg() > 1 { +			fmt.Printf("Usage: %s -passwd [OPTIONS] CIPHERDIR\n", PROGRAM_NAME)  			os.Exit(ERREXIT_USAGE)  		} -		args.mountpoint, _ = filepath.Abs(flagSet.Arg(1)) -		err := checkDirEmpty(args.mountpoint) -		if err != nil { -			fmt.Printf("Invalid MOUNTPOINT: %v\n", err) -			os.Exit(ERREXIT_MOUNTPOINT) -		} +		changePassword(args.config) // does not return  	} -	args.cipherdir, _ = filepath.Abs(flagSet.Arg(0)) -	err := checkDir(args.cipherdir) -	if err != nil { -		fmt.Printf("Invalid CIPHERDIR: %v\n", err) -		os.Exit(ERREXIT_CIPHERDIR) -	} - -	var plaintextNames bool -	var cf *cryptfs.ConfFile -	var currentPassword string -	key := make([]byte, cryptfs.KEY_LEN) -	if args.zerokey { -		cryptfs.Info.Printf("Zerokey mode active: using all-zero dummy master key.\n") -		plaintextNames = args.plaintextnames -	} else if len(args.masterkey) > 0 { -		key = parseMasterKey(args.masterkey) -		cryptfs.Info.Printf("Using explicit master key.\n") -	} else { -		// Load config file -		cfname := filepath.Join(args.cipherdir, cryptfs.ConfDefaultName) -		_, err = os.Stat(cfname) -		if err != nil { -			fmt.Printf("Error: %s not found in CIPHERDIR\n", cryptfs.ConfDefaultName) -			fmt.Printf("Please run \"%s --init %s\" first\n", os.Args[0], flagSet.Arg(0)) -			os.Exit(ERREXIT_LOADCONF) -		} -		if args.passwd == true { -			fmt.Printf("Old password: ") -		} else { -			fmt.Printf("Password: ") -		} -		currentPassword = readPassword() -		cryptfs.Info.Printf("Decrypting master key... ") -		cryptfs.Warn.Disable() // Silence DecryptBlock() error messages on incorrect password -		key, cf, err = cryptfs.LoadConfFile(cfname, currentPassword) -		cryptfs.Warn.Enable() -		if err != nil { -			fmt.Println(err) -			os.Exit(ERREXIT_LOADCONF) -		} -		cryptfs.Info.Printf("done.\n") +	// Mount +	// Check mountpoint +	if flagSet.NArg() < 2 { +		usageText() +		os.Exit(ERREXIT_USAGE)  	} -	if args.passwd == true { -		fmt.Printf("Please enter the new password.\n") -		newPassword := readPasswordTwice() -		if newPassword == currentPassword { -			fmt.Printf("New and old passwords are identical\n") -			os.Exit(ERREXIT_PASSWORD) -		} -		cf.EncryptKey(key, newPassword) -		err := cf.WriteFile() -		if err != nil { -			fmt.Println(err) -			os.Exit(ERREXIT_INIT) -		} -		cryptfs.Info.Printf("Password changed.\n") -		os.Exit(0) +	args.mountpoint, err = filepath.Abs(flagSet.Arg(1)) +	if err != nil { +		fmt.Printf("Invalid MOUNTPOINT: %v\n", err) +		os.Exit(ERREXIT_MOUNTPOINT)  	} - -	if cf != nil { -		plaintextNames = cf.PlaintextNames() +	err = checkDirEmpty(args.mountpoint) +	if err != nil { +		fmt.Printf("Invalid MOUNTPOINT: %v\n", err) +		os.Exit(ERREXIT_MOUNTPOINT)  	} -	srv := pathfsFrontend(key, args.cipherdir, args.mountpoint, args.fusedebug, args.openssl, plaintextNames) - -	if args.zerokey == false && len(args.masterkey) == 0 { -		printMasterKey(key) -	} else if args.zerokey == true { -		cryptfs.Info.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.\n") -	} else if len(args.masterkey) > 0 { +	// Get master key +	var masterkey []byte +	if args.masterkey != "" { +		// "-masterkey" +		cryptfs.Info.Printf("Using explicit master key.\n") +		masterkey = parseMasterKey(args.masterkey)  		cryptfs.Info.Printf("THE MASTER KEY IS VISIBLE VIA \"ps -auxwww\", ONLY USE THIS MODE FOR EMERGENCIES.\n") +	} else if args.zerokey { +		// "-zerokey" +		cryptfs.Info.Printf("Using all-zero dummy master key.\n") +		cryptfs.Info.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.\n") +		masterkey = make([]byte, cryptfs.KEY_LEN) +	} else { +		// Load master key from config file +		var confFile *cryptfs.ConfFile +		masterkey, confFile = loadConfig(args.config) +		printMasterKey(masterkey) +		args.plaintextnames = confFile.PlaintextNames()  	} - +	// Initialize FUSE server +	srv := pathfsFrontend(masterkey, args.cipherdir, args.mountpoint, args.fusedebug, args.openssl, args.plaintextnames)  	cryptfs.Info.Println("Filesystem ready.") -	// Send USR1 notification +	// We are ready - send USR1 signal to our parent  	if args.notifypid > 0 {  		sendUsr1(args.notifypid)  	} -	// Wait for SIGING in the background and unmount ourselves if we get it -	// This prevents a dangling "Transport endpoint is not connected" mountpoint +	// Wait for SIGINT in the background and unmount ourselves if we get it. +	// This prevents a dangling "Transport endpoint is not connected" mountpoint.  	handleSigint(srv, args.mountpoint)  	// Jump into server loop. Returns when it gets an umount request from the kernel.  	srv.Serve() -	// main returns with code 0 +	// main exits with code 0  } +// pathfsFrontend - initialize FUSE server based on go-fuse's PathFS +// Calls os.Exit on errors  func pathfsFrontend(key []byte, cipherdir string, mountpoint string,  	debug bool, openssl bool, plaintextNames bool) *fuse.Server { diff --git a/masterkey.go b/masterkey.go index a1889b4..2062997 100644 --- a/masterkey.go +++ b/masterkey.go @@ -37,7 +37,8 @@ paper and store it in a drawer.  `, hChunked)  } -// Parse a hex-encoded master key that was passed on the command line +// parseMasterKey - Parse a hex-encoded master key that was passed on the command line +// Calls os.Exit on failure  func parseMasterKey(masterkey string) []byte {  	masterkey = strings.Replace(masterkey, "-", "", -1)  	key, err := hex.DecodeString(masterkey)  | 
