From 58592330dc33e194e55fa942d3f375124839f058 Mon Sep 17 00:00:00 2001 From: Jakob Unterwurzacher Date: Sat, 14 Nov 2015 21:25:10 +0100 Subject: Refactor cli argument handling Also, add the "-config" option for storing gocryptfs.conf outside of CIPHERDIR. --- main.go | 245 ++++++++++++++++++++++++++++++++++++---------------------------- 1 file changed, 140 insertions(+), 105 deletions(-) (limited to 'main.go') diff --git a/main.go b/main.go index 0f3fb4f..ece032e 100644 --- a/main.go +++ b/main.go @@ -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 { -- cgit v1.2.3