// pathfsFrontend - initialize gocryptfs/pathfs_frontend // Calls os.Exit on errors func pathfsFrontend(key []byte, args argContainer, confFile *cryptfs.ConfFile) *fuse.Server { // Reconciliate CLI and config file arguments into a Args struct that is passed to the // filesystem implementation frontendArgs := pathfs_frontend.Args{ Cipherdir: args.cipherdir, Masterkey: key, OpenSSL: args.openssl, PlaintextNames: args.plaintextnames, DirIV: args.diriv, EMENames: args.emenames, GCMIV128: args.gcmiv128, } // confFile is nil when "-zerokey" or "-masterkey" was used if confFile != nil { // Settings from the config file override command line args frontendArgs.PlaintextNames = confFile.IsFeatureFlagSet(cryptfs.FlagPlaintextNames) frontendArgs.DirIV = confFile.IsFeatureFlagSet(cryptfs.FlagDirIV) frontendArgs.EMENames = confFile.IsFeatureFlagSet(cryptfs.FlagEMENames) frontendArgs.GCMIV128 = confFile.IsFeatureFlagSet(cryptfs.FlagGCMIV128) } // EMENames implies DirIV, both on the command line and in the config file. if frontendArgs.EMENames { frontendArgs.DirIV = true } // PlainTexnames disables both EMENames and DirIV if frontendArgs.PlaintextNames { frontendArgs.DirIV = false frontendArgs.EMENames = false } cryptfs.Debug.Printf("frontendArgs: ") cryptfs.Debug.JSONDump(frontendArgs) finalFs := pathfs_frontend.NewFS(frontendArgs) pathFsOpts := &pathfs.PathNodeFsOptions{ClientInodes: true} pathFs := pathfs.NewPathNodeFs(finalFs, pathFsOpts) fuseOpts := &nodefs.Options{ // These options are to be compatible with libfuse defaults, // making benchmarking easier. NegativeTimeout: time.Second, AttrTimeout: time.Second, EntryTimeout: time.Second, } conn := nodefs.NewFileSystemConnector(pathFs.Root(), fuseOpts) var mOpts fuse.MountOptions mOpts.AllowOther = false // Set values shown in "df -T" and friends // First column, "Filesystem" mOpts.Options = append(mOpts.Options, "fsname="+args.cipherdir) // Second column, "Type", will be shown as "fuse." + Name mOpts.Name = "gocryptfs" srv, err := fuse.NewServer(conn.RawFS(), args.mountpoint, &mOpts) if err != nil { fmt.Printf("Mount failed: %v", err) os.Exit(ERREXIT_MOUNT) } srv.SetDebug(args.fusedebug) // All FUSE file and directory create calls carry explicit permission // information. We need an unrestricted umask to create the files and // directories with the requested permissions. syscall.Umask(0000) return srv }
func main() { runtime.GOMAXPROCS(4) // Parse command line arguments var debug, init, zerokey, fusedebug, openssl, passwd, foreground, version bool var masterkey, mountpoint, cipherdir string flag.Usage = usageText flag.BoolVar(&debug, "debug", false, "Enable debug output") flag.BoolVar(&fusedebug, "fusedebug", false, "Enable fuse library debug output") flag.BoolVar(&init, "init", false, "Initialize encrypted directory") flag.BoolVar(&zerokey, "zerokey", false, "Use all-zero dummy master key") flag.BoolVar(&openssl, "openssl", true, "Use OpenSSL instead of built-in Go crypto") flag.BoolVar(&passwd, "passwd", false, "Change password") flag.BoolVar(&foreground, "f", false, "Stay in the foreground") flag.BoolVar(&version, "version", false, "Print version and exit") flag.StringVar(&masterkey, "masterkey", "", "Mount with explicit master key") var cpuprofile = flag.String("cpuprofile", "", "Write cpu profile to specified file") flag.Parse() if version { fmt.Printf("%s %s; ", PROGRAM_NAME, GitVersion) fmt.Printf("on-disk format %d\n", cryptfs.HEADER_CURRENT_VERSION) os.Exit(0) } if !foreground { daemonize() // does not return } if *cpuprofile != "" { f, err := os.Create(*cpuprofile) if err != nil { fmt.Println(err) os.Exit(ERREXIT_INIT) } fmt.Printf("Writing CPU profile to %s\n", *cpuprofile) pprof.StartCPUProfile(f) defer pprof.StopCPUProfile() } if debug { cryptfs.Debug.Enable() cryptfs.Debug.Printf("Debug output enabled\n") } if openssl == false { fmt.Printf("Openssl disabled\n") } if init { if flag.NArg() != 1 { fmt.Printf("Usage: %s --init CIPHERDIR\n", PROGRAM_NAME) os.Exit(ERREXIT_USAGE) } initDir(flag.Arg(0)) // does not return } if passwd { if flag.NArg() != 1 { fmt.Printf("Usage: %s --passwd CIPHERDIR\n", PROGRAM_NAME) os.Exit(ERREXIT_USAGE) } } else { // Normal mount if flag.NArg() < 2 { usageText() os.Exit(ERREXIT_USAGE) } mountpoint, _ = filepath.Abs(flag.Arg(1)) err := checkDirEmpty(mountpoint) if err != nil { fmt.Printf("Invalid MOUNTPOINT: %v\n", err) os.Exit(ERREXIT_MOUNTPOINT) } } cipherdir, _ = filepath.Abs(flag.Arg(0)) err := checkDir(cipherdir) if err != nil { fmt.Printf("Invalid CIPHERDIR: %v\n", err) os.Exit(ERREXIT_CIPHERDIR) } var cf *cryptfs.ConfFile var currentPassword string key := make([]byte, cryptfs.KEY_LEN) if zerokey { fmt.Printf("Zerokey mode active: using all-zero dummy master key.\n") } else if len(masterkey) > 0 { key = parseMasterKey(masterkey) fmt.Printf("Using explicit master key.\n") } else { cfname := filepath.Join(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], flag.Arg(0)) os.Exit(ERREXIT_LOADCONF) } if passwd == true { fmt.Printf("Old password: "******"Password: "******"\nDecrypting 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) } fmt.Printf("done.\n") } if 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) } fmt.Printf("Password changed.\n") os.Exit(0) } srv := pathfsFrontend(key, cipherdir, mountpoint, fusedebug, openssl) if zerokey == false && len(masterkey) == 0 { printMasterKey(key) } else if zerokey == true { fmt.Printf("ZEROKEY MODE PROVIDES NO SECURITY AT ALL AND SHOULD ONLY BE USED FOR TESTING.\n") } else if len(masterkey) > 0 { fmt.Printf("THE MASTER KEY IS VISIBLE VIA \"ps -auxwww\", ONLY USE THIS MODE FOR EMERGENCIES.\n") } fmt.Println("Filesystem ready.") // Send notification to our parent sendUsr1() // Wait for SIGING in the background and unmount ourselves if we get it // This prevents a dangling "Transport endpoint is not connected" mountpoint handleSigint(srv, mountpoint) // Jump into server loop. Returns when it gets an umount request from the kernel. srv.Serve() // main returns with code 0 }