Skip to content

Commit 9d79315

Browse files
committed
wincred: store encoding used as attribute
Signed-off-by: Sebastiaan van Stijn <github@gone.nl>
1 parent 6a5e3dd commit 9d79315

1 file changed

Lines changed: 24 additions & 38 deletions

File tree

wincred/wincred.go

Lines changed: 24 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ package wincred
44

55
import (
66
"bytes"
7-
"encoding/binary"
7+
"errors"
8+
"fmt"
89
"net/url"
9-
"unicode/utf16"
1010

1111
winc "github.com/danieljoos/wincred"
1212
"golang.org/x/text/encoding/unicode"
@@ -25,7 +25,10 @@ func (h Wincred) Add(creds *credentials.Credentials) error {
2525
g := winc.NewGenericCredential(creds.ServerURL)
2626
g.UserName = creds.Username
2727
g.Persist = winc.PersistLocalMachine
28-
g.Attributes = []winc.CredentialAttribute{{Keyword: "label", Value: credsLabels}}
28+
g.Attributes = []winc.CredentialAttribute{
29+
{Keyword: "label", Value: credsLabels},
30+
{Keyword: "encoding", Value: []byte("utf16le")},
31+
}
2932

3033
encoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewEncoder()
3134
blob, _, err := transform.Bytes(encoder, []byte(creds.Secret))
@@ -66,21 +69,23 @@ func (h Wincred) Get(serverURL string) (string, string, error) {
6669

6770
for _, attr := range g.Attributes {
6871
if attr.Keyword == "label" && bytes.Equal(attr.Value, []byte(credentials.CredsLabel)) {
69-
creds := string(g.CredentialBlob)
70-
7172
// Older versions of the wincred credential-helper stored the password blob
7273
// as raw string bytes. Newer versions store it as UTF-16LE.
7374
//
74-
// The credential blob does not include encoding metadata, so we cannot
75-
// reliably distinguish legacy entries from UTF-16LE entries in all cases.
76-
// For backward compatibility, keep the legacy raw-byte form by default and
77-
// only use the UTF-16LE-decoded form when it round-trips cleanly.
78-
//
7975
// See https://github.com/docker/docker-credential-helpers/pull/335
80-
if p, ok := tryDecodeUTF16LE(g.CredentialBlob); ok {
81-
creds = p
76+
switch enc := credentialEncoding(g.Attributes); enc {
77+
case "utf16le":
78+
decoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
79+
creds, _, err := transform.Bytes(decoder, g.CredentialBlob)
80+
if err != nil {
81+
return "", "", fmt.Errorf("decoding credentials: %w", err)
82+
}
83+
return g.UserName, string(creds), nil
84+
case "":
85+
return g.UserName, string(g.CredentialBlob), nil
86+
default:
87+
return "", "", errors.New("unsupported credential encoding: " + enc)
8288
}
83-
return g.UserName, creds, nil
8489
}
8590
}
8691
return "", "", credentials.NewErrCredentialsNotFound()
@@ -171,30 +176,11 @@ func (h Wincred) List() (map[string]string, error) {
171176
return resp, nil
172177
}
173178

174-
func tryDecodeUTF16LE(blob []byte) (string, bool) {
175-
if len(blob)%2 != 0 {
176-
return "", false
177-
}
178-
179-
decoder := unicode.UTF16(unicode.LittleEndian, unicode.IgnoreBOM).NewDecoder()
180-
s, _, err := transform.String(decoder, string(blob))
181-
if err != nil {
182-
return "", false
183-
}
184-
185-
// roundtrip the value to check if it was indeed valid UTF-16LE.
186-
if string(encodeUTF16LE(s)) != string(blob) {
187-
return "", false
188-
}
189-
190-
return s, true
191-
}
192-
193-
func encodeUTF16LE(s string) []byte {
194-
u16 := utf16.Encode([]rune(s))
195-
buf := make([]byte, len(u16)*2)
196-
for i, r := range u16 {
197-
binary.LittleEndian.PutUint16(buf[i*2:], r)
179+
func credentialEncoding(attrs []winc.CredentialAttribute) string {
180+
for _, attr := range attrs {
181+
if attr.Keyword == "encoding" {
182+
return string(attr.Value)
183+
}
198184
}
199-
return buf
185+
return ""
200186
}

0 commit comments

Comments
 (0)