104 lines
2.4 KiB
Go
104 lines
2.4 KiB
Go
package keyring
|
|
|
|
import (
|
|
"strings"
|
|
"syscall"
|
|
|
|
"github.com/danieljoos/wincred"
|
|
)
|
|
|
|
type windowsKeychain struct{}
|
|
|
|
// Get gets a secret from the keyring given a service name and a user.
|
|
func (k windowsKeychain) Get(service, username string) (string, error) {
|
|
cred, err := wincred.GetGenericCredential(k.credName(service, username))
|
|
if err != nil {
|
|
if err == syscall.ERROR_NOT_FOUND {
|
|
return "", ErrNotFound
|
|
}
|
|
return "", err
|
|
}
|
|
|
|
return string(cred.CredentialBlob), nil
|
|
}
|
|
|
|
// Set stores stores user and pass in the keyring under the defined service
|
|
// name.
|
|
func (k windowsKeychain) Set(service, username, password string) error {
|
|
// password may not exceed 2560 bytes (https://github.com/jaraco/keyring/issues/540#issuecomment-968329967)
|
|
if len(password) > 2560 {
|
|
return ErrSetDataTooBig
|
|
}
|
|
|
|
// service may not exceed 512 bytes (might need more testing)
|
|
if len(service) >= 512 {
|
|
return ErrSetDataTooBig
|
|
}
|
|
|
|
// service may not exceed 32k but problems occur before that
|
|
// so we limit it to 30k
|
|
if len(service) > 1024*30 {
|
|
return ErrSetDataTooBig
|
|
}
|
|
|
|
cred := wincred.NewGenericCredential(k.credName(service, username))
|
|
cred.UserName = username
|
|
cred.CredentialBlob = []byte(password)
|
|
return cred.Write()
|
|
}
|
|
|
|
// Delete deletes a secret, identified by service & user, from the keyring.
|
|
func (k windowsKeychain) Delete(service, username string) error {
|
|
cred, err := wincred.GetGenericCredential(k.credName(service, username))
|
|
if err != nil {
|
|
if err == syscall.ERROR_NOT_FOUND {
|
|
return ErrNotFound
|
|
}
|
|
return err
|
|
}
|
|
|
|
return cred.Delete()
|
|
}
|
|
|
|
func (k windowsKeychain) DeleteAll(service string) error {
|
|
// if service is empty, do nothing otherwise it might accidentally delete all secrets
|
|
if service == "" {
|
|
return ErrNotFound
|
|
}
|
|
|
|
creds, err := wincred.List()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
prefix := k.credName(service, "")
|
|
deletedCount := 0
|
|
|
|
for _, cred := range creds {
|
|
if strings.HasPrefix(cred.TargetName, prefix) {
|
|
genericCred, err := wincred.GetGenericCredential(cred.TargetName)
|
|
if err != nil {
|
|
if err != syscall.ERROR_NOT_FOUND {
|
|
return err
|
|
}
|
|
} else {
|
|
err := genericCred.Delete()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
deletedCount++
|
|
}
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// credName combines service and username to a single string.
|
|
func (k windowsKeychain) credName(service, username string) string {
|
|
return service + ":" + username
|
|
}
|
|
|
|
func init() {
|
|
provider = windowsKeychain{}
|
|
}
|