func saveServices(passwd string, services *Services) { sort.Sort(services) decryptedBuffer := bytes.NewBuffer(nil) err := json.NewEncoder(decryptedBuffer).Encode(services) gobro.CheckErr(err) blockCipher, err := blowfish.NewCipher([]byte(passwd)) gobro.CheckErr(err) iv := make([]byte, 8) _, err = rand.Read(iv) gobro.CheckErr(err) enc := decryptedBuffer.Bytes() cipher.NewCFBEncrypter(blockCipher, iv).XORKeyStream(enc, enc) buff := bytes.NewBuffer(iv) _, err = buff.Write(enc) gobro.CheckErr(err) enc = append(iv, enc...) base64EncodedLen := base64.StdEncoding.EncodedLen(len(enc)) base64Enc := make([]byte, base64EncodedLen) base64.StdEncoding.Encode(base64Enc, enc) err = ioutil.WriteFile(passwdFileName(), base64Enc, os.ModePerm) gobro.CheckErr(err) }
func changePassword(args []string) { if passwd == nil { pw, err := gopass.GetPass(" Current password: "******"" { fmt.Fprintln(os.Stderr, "Invalid password") return } } services, err := loadServices(getPasswd()) gobro.CheckErr(err, "Password invalid") pw2, err := gopass.GetPass(" New Password: "******"" { fmt.Fprintln(os.Stderr, "Invalid password") return } pw3, err := gopass.GetPass(" Repeat Password: "******"" { fmt.Fprintln(os.Stderr, "Invalid password") return } if pw2 != pw3 { fmt.Fprintln(os.Stderr, "Passwords don't match") return } saveServices(pw2, services) passwd = &pw2 }
func (gen *PasswordGenerator) loadDictionary() { dictFile, err := os.Open("/usr/share/dict/words") gobro.CheckErr(err) dictBytes, err := ioutil.ReadAll(dictFile) gobro.CheckErr(err) gen.dictionary = strings.Split(string(dictBytes), "\n") }
func (gen *PasswordGenerator) generateWithWords() string { // use /usr/share/dict dictFile, err := os.Open("/usr/share/dict/words") gobro.CheckErr(err) dictBytes, err := ioutil.ReadAll(dictFile) gobro.CheckErr(err) words := strings.Split(string(dictBytes), "\n") password := "" maxSubscript := big.NewInt(int64(len(words))) for len(password) < gen.length { subscript, _ := rand.Int(rand.Reader, maxSubscript) password += strings.Title(words[int(subscript.Int64())]) } return password }
func initialize(args []string) { _, err := loadConfig() if err != nil { u, err := user.Current() gobro.CheckErr(err) saveConfig(Config{PasswdFile: u.HomeDir + "/.passman"}) } services, err := loadServices(getPasswd()) if err == nil && services != nil && services.Len() > 0 { overwrite, err := commander.Prompt(" Overwrite existing password file? (y/N): ") if err != nil || strings.ToLower(overwrite) != "y" { return } } saveServices(getPasswd(), new(Services)) _, err = loadServices(getPasswd()) // just to test that we have saved it properly gobro.CheckErr(err, "Unable to read password file") }
func passwdFileName() string { config, err := loadConfig() gobro.CheckErr(err, "Unable to load config file") if config.PasswdFile == "" { fmt.Fprintln(os.Stderr, "The config file is missing or malformed.") os.Exit(1) } return config.PasswdFile }
func ls(args []string) { services, err := loadServices(getPasswd()) gobro.CheckErr(err, "Password invalid") if len(args) > 0 { services.Services = services.Search(args[0]) } for _, service := range services.Services { fmt.Printf(" %s\n", service.Name) } }
func add(args []string) { if len(args) == 0 { fmt.Fprintln(os.Stderr, "Usage: passman add <service> [<options>]\n"+ " -g: Generate password\n "+ " Additional options:\n"+ " l: include lowercase characters\n"+ " u: include uppercase characters\n"+ " n: include numbers\n"+ " c: include special characters\n"+ " w: generate a password using random words from the dictionary\n"+ " \\d+$: password must be at least this long."+ " example: add -glun24 will produce a password using, lowercase, characters,\n"+ " uppercase characters, and numbers, and will be 24 characters long"+ " -p: Enter password\n "+ " -m: attach metadata\n") return } name := args[0] services, err := loadServices(getPasswd()) gobro.CheckErr(err, "Password invalid") service := services.Get(name) service.Name = name generateParams, _ := strarr.FindMatchWithRegex(args, "-g.*") if generateParams != "" { service.Password = NewPasswordGenerator(generateParams).generate() } if strarr.Contains(args, "-p") { prompt := fmt.Sprintf(" Password for %s: ", service.Name) password, err := gopass.GetPass(prompt) gobro.CheckErr(err) service.Password = password } if strarr.Contains(args, "-m") { service.Meta, _ = commander.Prompt(fmt.Sprintf(" Meta for %s: ", service.Name)) } services.Add(service) saveServices(getPasswd(), services) }
func rm(args []string) { if len(args) == 0 { fmt.Fprintln(os.Stderr, "Usage: passman rm <services>") return } services, err := loadServices(getPasswd()) gobro.CheckErr(err, "Password invalid") for _, name := range args { services.Remove(name) } saveServices(getPasswd(), services) }
func getPasswd() string { if passwd == nil { pw, err := gopass.GetPass("Password: "******"" { fmt.Fprintln(os.Stderr, "Invalid password") os.Exit(1) } passwd = &pw } return *passwd }
func show(args []string) { prefix := "" if len(args) > 0 { prefix = args[0] } services, err := loadServices(getPasswd()) gobro.CheckErr(err, "Password invalid") for _, service := range services.Search(prefix) { fmt.Printf(" %20s: %s %s\n", service.Name, service.Password, service.Meta) } fmt.Println("") }
func cp(args []string) { if len(args) != 1 { fmt.Fprintln(os.Stderr, "Usage: passman cp <service>") return } services, err := loadServices(getPasswd()) gobro.CheckErr(err, "Password invalid") service := services.Get(args[0]) if service.Name != "" { clipboard.WriteAll(service.Password) } else { fmt.Printf("'%s' not found\n", args[0]) } }
func interactive(args []string) { cm := commandMap() _, err := loadServices(getPasswd()) gobro.CheckErr(err, "Password invalid") for { cmd, err := commander.Prompt("passman$ ") if err != nil { return } if cmd == "" { continue } args := make([]string, 0, 3) args = append(args, "passman") args = append(args, strings.Split(cmd, " ")...) cm.Run(args) } }
func NewPasswordGenerator(config string) *PasswordGenerator { regexp, err := regexp.Compile("\\d+$") gobro.CheckErr(err) passwordLength := 24 // default lengthBytes := regexp.Find([]byte(config)) if len(lengthBytes) > 0 { length, err := strconv.Atoi(string(lengthBytes)) if err == nil { passwordLength = length } } lowercase := strings.Contains(config, "l") uppercase := strings.Contains(config, "u") numbers := strings.Contains(config, "n") characters := strings.Contains(config, "c") words := strings.Contains(config, "w") if !(words || lowercase || uppercase || numbers || characters) { // Default values when no options specified lowercase = true uppercase = true numbers = true } generator := &PasswordGenerator{ lowercase: lowercase, uppercase: uppercase, numbers: numbers, characters: characters, words: words, length: passwordLength, } if words { generator.loadDictionary() } else { generator.loadCharacterTypes() } return generator }
func loadServices(passwd string) (*Services, error) { base64Enc, err := ioutil.ReadFile(passwdFileName()) if err != nil { return nil, err } enc := make([]byte, base64.StdEncoding.DecodedLen(len(base64Enc))) base64.StdEncoding.Decode(enc, base64Enc) blockCipher, err := blowfish.NewCipher([]byte(passwd)) gobro.CheckErr(err) decrypted := make([]byte, len(enc)-8) cipher.NewCFBDecrypter(blockCipher, enc[:8]).XORKeyStream(decrypted, enc[8:]) services := new(Services) err = json.NewDecoder(bytes.NewBuffer(decrypted)).Decode(services) if err != nil { return nil, errors.New("Invalid password") } return services, nil }
func configFileName() string { u, err := user.Current() gobro.CheckErr(err) return u.HomeDir + "/.passman-config" }