diff --git a/go.mod b/go.mod index e55f9625475..17b5f63f3ab 100644 --- a/go.mod +++ b/go.mod @@ -27,18 +27,17 @@ require ( github.com/aws/aws-sdk-go-v2/service/ec2 v1.16.0 github.com/aws/aws-sdk-go-v2/service/sts v1.10.0 github.com/beevik/etree v1.1.0 - github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 github.com/coreos/go-oidc v2.1.0+incompatible github.com/coreos/go-semver v0.3.0 github.com/creack/pty v1.1.18 github.com/denisenkom/go-mssqldb v0.11.0 - github.com/duo-labs/webauthn v0.0.0-20210727191636-9f1b88ef44cc + github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951 github.com/dustin/go-humanize v1.0.0 github.com/elastic/go-elasticsearch/v8 v8.4.0 github.com/flynn/hid v0.0.0-20190502022136-f1b9b6cc019a github.com/flynn/u2f v0.0.0-20180613185708-15554eb68e5d github.com/fsouza/fake-gcs-server v1.19.5 - github.com/fxamacker/cbor/v2 v2.3.0 + github.com/fxamacker/cbor/v2 v2.4.0 github.com/ghodss/yaml v1.0.0 github.com/gizak/termui/v3 v3.1.0 github.com/go-ldap/ldap/v3 v3.4.1 @@ -96,7 +95,6 @@ require ( github.com/sirupsen/logrus v1.8.1 github.com/snowflakedb/gosnowflake v1.6.9 github.com/stretchr/testify v1.8.0 - github.com/tstranex/u2f v0.0.0-20160508205855-eb799ce68da4 github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb github.com/vulcand/predicate v1.2.0 go.etcd.io/etcd/api/v3 v3.5.1 @@ -185,10 +183,10 @@ require ( github.com/cenkalti/backoff/v4 v4.1.3 // indirect github.com/cespare/xxhash/v2 v2.1.2 // indirect github.com/chai2010/gettext-go v0.0.0-20160711120539-c6fed771bfd5 // indirect + github.com/cloudflare/cfssl v0.0.0-20190726000631-633726f6bcb7 // indirect github.com/coreos/go-systemd/v22 v22.3.2 // indirect github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f // indirect github.com/davecgh/go-spew v1.1.1 // indirect - github.com/dgrijalva/jwt-go v3.2.0+incompatible // indirect github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect github.com/elastic/elastic-transport-go/v8 v8.1.0 // indirect github.com/emicklei/go-restful v2.9.5+incompatible // indirect @@ -207,6 +205,7 @@ require ( github.com/go-openapi/jsonreference v0.19.5 // indirect github.com/go-openapi/swag v0.19.14 // indirect github.com/golang-jwt/jwt v3.2.2+incompatible // indirect + github.com/golang-jwt/jwt/v4 v4.2.0 // indirect github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe // indirect github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect @@ -274,7 +273,6 @@ require ( github.com/rogpeppe/go-internal v1.8.0 // indirect github.com/russross/blackfriday v1.5.2 // indirect github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 // indirect - github.com/satori/go.uuid v1.2.0 // indirect github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 // indirect github.com/siddontang/go v0.0.0-20180604090527-bdc77568d726 // indirect github.com/siddontang/go-log v0.0.0-20180807004314-8d05993dda07 // indirect diff --git a/go.sum b/go.sum index 1592db757d6..5c554c40652 100644 --- a/go.sum +++ b/go.sum @@ -300,8 +300,8 @@ github.com/dnaeon/go-vcr v1.2.0 h1:zHCHvJYTMh1N7xnV7zf1m1GPBF9Ad0Jk/whtQ1663qI= github.com/dnaeon/go-vcr v1.2.0/go.mod h1:R4UdLID7HZT3taECzJs4YgbbH6PIGXB6W/sc5OLb6RQ= github.com/docker/distribution v2.8.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= -github.com/duo-labs/webauthn v0.0.0-20210727191636-9f1b88ef44cc h1:mLNknBMRNrYNf16wFFUyhSAe1tISZN7oAfal4CZ2OxY= -github.com/duo-labs/webauthn v0.0.0-20210727191636-9f1b88ef44cc/go.mod h1:/X2OJiJxjQ7alqWZqX9EtBTmZc+4qQ0LvZ1k5wP67RM= +github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951 h1:17esZ09oW+29rklBtCVphIguql2u3NxYH2OasFPPZoo= +github.com/duo-labs/webauthn v0.0.0-20220122034320-81aea484c951/go.mod h1:nHy3JdztZWcsjenDeBuE8gn171OAwg12LBN027UP5AE= github.com/dustin/go-humanize v0.0.0-20171111073723-bb3d318650d4/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -361,8 +361,8 @@ github.com/fsouza/fake-gcs-server v1.19.5 h1:YTBDIWtCSF6T9x5Hm0lw0clIF7/XTZIQ58g github.com/fsouza/fake-gcs-server v1.19.5/go.mod h1:Jd6DuVGNvNXC+pEoAbRDihNaEIjIW9UuRVPEvBhHJek= github.com/fvbommel/sortorder v1.0.1/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fxamacker/cbor/v2 v2.2.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= -github.com/fxamacker/cbor/v2 v2.3.0 h1:aM45YGMctNakddNNAezPxDUpv38j44Abh+hifNuqXik= -github.com/fxamacker/cbor/v2 v2.3.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= +github.com/fxamacker/cbor/v2 v2.4.0 h1:ri0ArlOR+5XunOP8CRUowT0pSJOwhW098ZCUyskZD88= +github.com/fxamacker/cbor/v2 v2.4.0/go.mod h1:TA1xS00nchWmaBnEIxPSE5oHLuJBAVvqrtAnWBwBCVo= github.com/gabriel-vasile/mimetype v1.4.0 h1:Cn9dkdYsMIu56tGho+fqzh7XmvY2YyGU0FnbhiOsEro= github.com/gabriel-vasile/mimetype v1.4.0/go.mod h1:fA8fi6KUiG7MgQQ+mEWotXoEOvmxRtOJlERCzSmRvr8= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= @@ -426,11 +426,12 @@ github.com/gofrs/flock v0.8.1/go.mod h1:F1TvTiK9OcQqauNUHlbJvyl9Qa1QvF/gOUDKA14j github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= github.com/gogo/googleapis v1.1.0/go.mod h1:gf4bu3Q80BeJ6H1S1vYPm8/ELATdvryBaNFGgqEef3s= -github.com/golang-jwt/jwt v3.2.1+incompatible h1:73Z+4BJcrTC+KczS6WvTPvRGOp1WmfEP4Q1lOd9Z/+c= github.com/golang-jwt/jwt v3.2.1+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= github.com/golang-jwt/jwt v3.2.2+incompatible h1:IfV12K8xAKAnZqdXVzCZ+TOjboZ2keLg81eXfW3O+oY= github.com/golang-jwt/jwt v3.2.2+incompatible/go.mod h1:8pz2t5EyA70fFQQSrl6XZXzqecmYZeUEB8OUGHkxJ+I= +github.com/golang-jwt/jwt/v4 v4.1.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe h1:lXe2qZdvpiX5WZkZR4hgp4KJVfY3nMkvmwbVkpv1rVY= github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang-sql/sqlexp v0.0.0-20170517235910-f1bb20e5a188 h1:+eHOFJl1BaXrQxKX+T06f78590z4qA2ZzBTqahsKSE4= @@ -1022,7 +1023,6 @@ github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f/go.mod h1:sm1tb github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46 h1:GHRpF1pTW19a8tTFrMLUcfWwyC0pnifVo2ClaLq+hP8= github.com/ryszard/goskiplist v0.0.0-20150312221310-2dfbae5fcf46/go.mod h1:uAQ5PCi+MFsC7HjREoAz1BU+Mq60+05gifQSsHSDG/8= github.com/samuel/go-zookeeper v0.0.0-20190923202752-2cc03de413da/go.mod h1:gi+0XIa01GRL2eRQVjQkKGqKF3SF9vZR/HnPullcV2E= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc= github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM= @@ -1097,8 +1097,6 @@ github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhV github.com/tmc/grpc-websocket-proxy v0.0.0-20170815181823-89b8d40f7ca8/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20190109142713-0ad062ec5ee5/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802/go.mod h1:ncp9v5uamzpCO7NfCPTXjqaC+bZgJeR0sMTm6dMHP7U= -github.com/tstranex/u2f v0.0.0-20160508205855-eb799ce68da4 h1:aR+lGR8m0zBjvDlHkHOCmdsk79ipIPeiP75GqUlywKM= -github.com/tstranex/u2f v0.0.0-20160508205855-eb799ce68da4/go.mod h1:eahSLaqAS0zsIEv80+vXT7WanXs7MQQDg3j3wGBSayo= github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb h1:Ywfo8sUltxogBpFuMOFRrrSifO788kAFxmvVw31PtQQ= github.com/ucarion/urlpath v0.0.0-20200424170820-7ccc79b76bbb/go.mod h1:ikPs9bRWicNw3S7XpJ8sK/smGwU9WcSVU3dy9qahYBM= github.com/urfave/cli v1.20.0/go.mod h1:70zkFmudgCuE/ngEzBv17Jvp/497gISqfk5gWijbERA= diff --git a/integration/hostuser_test.go b/integration/hostuser_test.go index f0725c50533..b747d726386 100644 --- a/integration/hostuser_test.go +++ b/integration/hostuser_test.go @@ -1,4 +1,3 @@ -// go:build linux //go:build linux // +build linux @@ -29,7 +28,6 @@ import ( "path/filepath" "testing" - "github.com/cloudflare/cfssl/log" "github.com/gravitational/teleport/api/types" "github.com/gravitational/teleport/lib/backend" "github.com/gravitational/teleport/lib/backend/lite" @@ -39,6 +37,8 @@ import ( "github.com/gravitational/teleport/lib/utils/host" "github.com/gravitational/trace" "github.com/stretchr/testify/require" + + log "github.com/sirupsen/logrus" ) const testuser = "teleport-testuser" diff --git a/lib/auth/mocku2f/mocku2f.go b/lib/auth/mocku2f/mocku2f.go index aaa643c1d41..1324374834f 100644 --- a/lib/auth/mocku2f/mocku2f.go +++ b/lib/auth/mocku2f/mocku2f.go @@ -22,7 +22,6 @@ package mocku2f */ import ( - "bytes" "crypto" "crypto/ecdsa" "crypto/elliptic" @@ -30,16 +29,12 @@ import ( "crypto/sha256" "crypto/x509" "crypto/x509/pkix" - "encoding/base64" "encoding/binary" - "encoding/json" "math/big" - "strings" "time" "github.com/duo-labs/webauthn/protocol" "github.com/gravitational/trace" - "github.com/tstranex/u2f" ) // u2fRegistrationFlags is fixed by the U2F standard. @@ -74,19 +69,6 @@ type Key struct { counter uint32 } -// The "websafe-base64 encoding" in the U2F specifications removes the padding -func decodeBase64(s string) ([]byte, error) { - for i := 0; i < len(s)%4; i++ { - s += "=" - } - return base64.URLEncoding.DecodeString(s) -} - -func encodeBase64(buf []byte) string { - s := base64.URLEncoding.EncodeToString(buf) - return strings.TrimRight(s, "=") -} - func selfSignPublicKey(keyToSign *ecdsa.PublicKey) (cert []byte, err error) { caPrivateKey, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { @@ -151,31 +133,6 @@ func (muk *Key) SetPasswordless() { muk.SetUV = true // UV required for passwordless. } -func (muk *Key) RegisterResponse(req *u2f.RegisterRequest) (*u2f.RegisterResponse, error) { - appIDHash := sha256.Sum256([]byte(req.AppID)) - - clientData := u2f.ClientData{ - Typ: "navigator.id.finishEnrollment", - Challenge: req.Challenge, - Origin: req.AppID, - } - clientDataJSON, err := json.Marshal(clientData) - if err != nil { - return nil, trace.Wrap(err) - } - clientDataHash := sha256.Sum256(clientDataJSON) - - res, err := muk.signRegister(appIDHash[:], clientDataHash[:]) - if err != nil { - return nil, trace.Wrap(err) - } - - return &u2f.RegisterResponse{ - RegistrationData: encodeBase64(res.RawResp), - ClientData: encodeBase64(clientDataJSON), - }, nil -} - // RegisterRaw signs low-level U2F registration data. // Most callers should use either RegisterResponse or SignCredentialCreation. func (muk *Key) RegisterRaw(appHash, challengeHash []byte) ([]byte, error) { @@ -229,39 +186,6 @@ func (muk *Key) signRegister(appIDHash, clientDataHash []byte) (*signRegisterRes }, nil } -func (muk *Key) SignResponse(req *u2f.SignRequest) (*u2f.SignResponse, error) { - rawKeyHandle, err := decodeBase64(req.KeyHandle) - if err != nil { - return nil, trace.Wrap(err) - } - if !bytes.Equal(rawKeyHandle, muk.KeyHandle) { - return nil, trace.CompareFailed("wrong keyHandle") - } - appIDHash := sha256.Sum256([]byte(req.AppID)) - - clientData := u2f.ClientData{ - Typ: "navigator.id.getAssertion", - Challenge: req.Challenge, - Origin: req.AppID, - } - clientDataJSON, err := json.Marshal(clientData) - if err != nil { - return nil, trace.Wrap(err) - } - clientDataHash := sha256.Sum256(clientDataJSON) - - res, err := muk.signAuthn(appIDHash[:], clientDataHash[:]) - if err != nil { - return nil, trace.Wrap(err) - } - - return &u2f.SignResponse{ - KeyHandle: req.KeyHandle, - SignatureData: encodeBase64(res.SignData), - ClientData: encodeBase64(clientDataJSON), - }, nil -} - // AuthenticateRaw signs low-level U2F authentication data. // Most callers should use either SignResponse or SignAssertion. func (muk *Key) AuthenticateRaw(appHash, challengeHash []byte) ([]byte, error) { diff --git a/lib/auth/webauthn/login.go b/lib/auth/webauthn/login.go index cfe0a2d038d..600816597dd 100644 --- a/lib/auth/webauthn/login.go +++ b/lib/auth/webauthn/login.go @@ -256,6 +256,17 @@ func (f *loginFlow) finish(ctx context.Context, user string, resp *CredentialAss } sessionData := sessionFromPB(sessionDataPB) + // Make sure _all_ credentials in the session are accounted for by the user. + // webauthn.ValidateLogin requires it. + for _, allowedCred := range sessionData.AllowedCredentialIDs { + if bytes.Equal(parsedResp.RawID, allowedCred) { + continue + } + u.credentials = append(u.credentials, wan.Credential{ + ID: allowedCred, + }) + } + // Create a WebAuthn matching the expected RPID and Origin, then verify the // signed challenge. web, err := newWebAuthn(webAuthnParams{ diff --git a/lib/auth/webauthn/register.go b/lib/auth/webauthn/register.go index 14beac1dad7..917d72ba4c1 100644 --- a/lib/auth/webauthn/register.go +++ b/lib/auth/webauthn/register.go @@ -175,15 +175,11 @@ func (f *RegistrationFlow) Begin(ctx context.Context, user string, passwordless if err != nil { return nil, trace.Wrap(err) } - credentialCreation, sessionData, err := web.BeginRegistration(u, wan.WithExclusions(exclusions)) + cc, sessionData, err := web.BeginRegistration(u, wan.WithExclusions(exclusions)) if err != nil { return nil, trace.Wrap(err) } - // Copy settings manually, the framework doesn't do it. - credentialCreation.Response.AuthenticatorSelection = web.Config.AuthenticatorSelection - sessionData.UserVerification = web.Config.AuthenticatorSelection.UserVerification - // TODO(codingllama): Send U2F App ID back in creation requests too. Useful to // detect duplicate devices. @@ -195,7 +191,7 @@ func (f *RegistrationFlow) Begin(ctx context.Context, user string, passwordless return nil, trace.Wrap(err) } - return (*CredentialCreation)(credentialCreation), nil + return (*CredentialCreation)(cc), nil } func upsertOrGetWebID(ctx context.Context, user string, identity RegistrationIdentity) ([]byte, error) {