Skip to content

Commit c16684b

Browse files
committed
wincred: add fallback for existing values
As older versions of the credential helper would store creds as raw bytes, we must expect the value to be a raw byte, not UTF-16LE encoded. Try decoding the value and check if it round-trips correctly to make sure we're actually dealing with UTF-16LE encoded creds. We should also consider setting a custom "encoding" attribute instead to detect what encoding was used to store the value. Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent becbf0f commit c16684b

1 file changed

Lines changed: 51 additions & 6 deletions

File tree

wincred/wincred.go

Lines changed: 51 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,8 +26,7 @@ func (h Wincred) Add(creds *credentials.Credentials) error {
2626
g.Persist = winc.PersistLocalMachine
2727
g.Attributes = []winc.CredentialAttribute{{Keyword: "label", Value: credsLabel}}
2828

29-
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
30-
blob, _, err := transform.Bytes(encoder, []byte(creds.Secret))
29+
blob, err := encodeUTF16LE(creds.Secret)
3130
if err != nil {
3231
return err
3332
}
@@ -65,10 +64,14 @@ func (h Wincred) Get(serverURL string) (string, string, error) {
6564

6665
for _, attr := range g.Attributes {
6766
if attr.Keyword == "label" && bytes.Equal(attr.Value, credsLabel) {
68-
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
69-
creds, _, err := transform.String(encoder, string(g.CredentialBlob))
70-
if err != nil {
71-
return "", "", err
67+
// Older versions of the wincred credential-helper stored the password blob
68+
// as raw string bytes. Newer versions store it as UTF-16LE. Try decoding from
69+
// UTF-16LE, otherwise assume creds were stored as raw bytes.
70+
//
71+
// See https://github.com/docker/docker-credential-helpers/pull/335
72+
creds := string(g.CredentialBlob)
73+
if p, ok := tryDecodeUTF16LE(g.CredentialBlob); ok {
74+
creds = p
7275
}
7376
return g.UserName, creds, nil
7477
}
@@ -160,3 +163,45 @@ func (h Wincred) List() (map[string]string, error) {
160163

161164
return resp, nil
162165
}
166+
167+
func tryDecodeUTF16LE(blob []byte) (string, bool) {
168+
if len(blob)%2 != 0 {
169+
return "", false
170+
}
171+
172+
decoded, err := decodeUTF16LE(blob)
173+
if err != nil {
174+
return "", false
175+
}
176+
177+
s := string(decoded)
178+
encoded, err := encodeUTF16LE(s)
179+
if err != nil {
180+
return "", false
181+
}
182+
183+
// round-trip the value to verify it was indeed valid UTF-16LE.
184+
if !bytes.Equal(encoded, blob) {
185+
return "", false
186+
}
187+
188+
return s, true
189+
}
190+
191+
func decodeUTF16LE(blob []byte) ([]byte, error) {
192+
decoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
193+
decoded, _, err := transform.Bytes(decoder, blob)
194+
if err != nil {
195+
return nil, err
196+
}
197+
return decoded, nil
198+
}
199+
200+
func encodeUTF16LE(s string) ([]byte, error) {
201+
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
202+
encoded, _, err := transform.Bytes(encoder, []byte(s))
203+
if err != nil {
204+
return nil, err
205+
}
206+
return encoded, nil
207+
}

0 commit comments

Comments
 (0)