Skip to content

Commit 08d5a83

Browse files
authored
Attempt to fix the webauthn migration again - part 3 (#18770) (#18771)
Backport #18770 v208.go is seriously broken as it misses an ID() check. We need to no-op and remigrate all of the u2f keys. See #18756 Signed-off-by: Andrew Thornton <art27@cantab.net>
1 parent ad78954 commit 08d5a83

File tree

10 files changed

+190
-253
lines changed

10 files changed

+190
-253
lines changed

models/auth/webauthn.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type WebAuthnCredential struct {
4343
Name string
4444
LowerName string `xorm:"unique(s)"`
4545
UserID int64 `xorm:"INDEX unique(s)"`
46-
CredentialID string `xorm:"INDEX"`
46+
CredentialID string `xorm:"INDEX VARCHAR(410)"`
4747
PublicKey []byte
4848
AttestationType string
4949
AAGUID []byte

models/migrations/fixtures/Test_increaseCredentialIDTo410/expected_webauthn_credential.yml renamed to models/migrations/fixtures/Test_remigrateU2FCredentials/expected_webauthn_credential.yml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44
-
55
id: 2
66
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
7+
-
8+
id: 3
9+
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="
710
-
811
id: 4
9-
credential_id: "THIS SHOULD NOT CHAGNGE"
12+
credential_id: "TVHE44TOH7DF7V48SEAIT3EMMJ7TGBOQ289E5AQB34S98LFCUFJ7U2NAVI8RJG6K2F4TC8AQ8KBNO7AGEOQOL9NE43GR63HTEHJSLOG="

models/migrations/fixtures/Test_increaseCredentialIDTo410/webauthn_credential.yml renamed to models/migrations/fixtures/Test_remigrateU2FCredentials/webauthn_credential.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
lower_name: "u2fkey-wrong-user-id"
2424
name: "u2fkey-wrong-user-id"
2525
user_id: 1
26-
credential_id: "THIS SHOULD NOT CHAGNGE"
26+
credential_id: "THIS SHOULD CHANGE"
2727
public_key: 0x040d0967a2cad045011631187576492a0beb5b377954b4f694c5afc8bdf25270f87f09a9ab6ce9c282f447ba71b2f2bae2105b32b847e0704f310f48644e3eddf2
2828
attestation_type: 'fido-u2f'
2929
sign_count: 1

models/migrations/migrations.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -367,11 +367,13 @@ var migrations = []Migration{
367367
// v206 -> v207
368368
NewMigration("Add authorize column to team_unit table", addAuthorizeColForTeamUnit),
369369
// v207 -> v208
370-
NewMigration("Add webauthn table and migrate u2f data to webauthn", addWebAuthnCred),
370+
NewMigration("Add webauthn table and migrate u2f data to webauthn - NO-OPED", addWebAuthnCred),
371371
// v208 -> v209
372-
NewMigration("Use base32.HexEncoding instead of base64 encoding for cred ID as it is case insensitive", useBase32HexForCredIDInWebAuthnCredential),
372+
NewMigration("Use base32.HexEncoding instead of base64 encoding for cred ID as it is case insensitive - NO-OPED", useBase32HexForCredIDInWebAuthnCredential),
373373
// v209 -> v210
374-
NewMigration("Increase WebAuthentication CredentialID size to 410", increaseCredentialIDTo410),
374+
NewMigration("Increase WebAuthentication CredentialID size to 410 - NO-OPED", increaseCredentialIDTo410),
375+
// v210 -> v211
376+
NewMigration("v208 was completely broken - remigrate", remigrateU2FCredentials),
375377
}
376378

377379
// GetCurrentDBVersion returns the current db version

models/migrations/v207.go

Lines changed: 1 addition & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -5,87 +5,11 @@
55
package migrations
66

77
import (
8-
"crypto/elliptic"
9-
"encoding/base64"
10-
"strings"
11-
12-
"code.gitea.io/gitea/modules/timeutil"
13-
14-
"github.com/tstranex/u2f"
158
"xorm.io/xorm"
169
)
1710

1811
func addWebAuthnCred(x *xorm.Engine) error {
19-
20-
// Create webauthnCredential table
21-
type webauthnCredential struct {
22-
ID int64 `xorm:"pk autoincr"`
23-
Name string
24-
LowerName string `xorm:"unique(s)"`
25-
UserID int64 `xorm:"INDEX unique(s)"`
26-
CredentialID string `xorm:"INDEX VARCHAR(410)"` // CredentalID in U2F is at most 255bytes / 5 * 8 = 408 - add a few extra characters for safety
27-
PublicKey []byte
28-
AttestationType string
29-
AAGUID []byte
30-
SignCount uint32 `xorm:"BIGINT"`
31-
CloneWarning bool
32-
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
33-
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
34-
}
35-
if err := x.Sync2(&webauthnCredential{}); err != nil {
36-
return err
37-
}
38-
39-
// Now migrate the old u2f registrations to the new format
40-
type u2fRegistration struct {
41-
ID int64 `xorm:"pk autoincr"`
42-
Name string
43-
UserID int64 `xorm:"INDEX"`
44-
Raw []byte
45-
Counter uint32 `xorm:"BIGINT"`
46-
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
47-
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
48-
}
49-
50-
var start int
51-
regs := make([]*u2fRegistration, 0, 50)
52-
for {
53-
err := x.OrderBy("id").Limit(50, start).Find(&regs)
54-
if err != nil {
55-
return err
56-
}
57-
58-
for _, reg := range regs {
59-
parsed := new(u2f.Registration)
60-
err = parsed.UnmarshalBinary(reg.Raw)
61-
if err != nil {
62-
continue
63-
}
64-
65-
c := &webauthnCredential{
66-
ID: reg.ID,
67-
Name: reg.Name,
68-
LowerName: strings.ToLower(reg.Name),
69-
UserID: reg.UserID,
70-
CredentialID: base64.RawStdEncoding.EncodeToString(parsed.KeyHandle),
71-
PublicKey: elliptic.Marshal(elliptic.P256(), parsed.PubKey.X, parsed.PubKey.Y),
72-
AttestationType: "fido-u2f",
73-
AAGUID: []byte{},
74-
SignCount: reg.Counter,
75-
}
76-
77-
_, err := x.Insert(c)
78-
if err != nil {
79-
return err
80-
}
81-
}
82-
83-
if len(regs) < 50 {
84-
break
85-
}
86-
start += 50
87-
regs = regs[:0]
88-
}
12+
// NO-OP Don't migrate here - let v210 do this.
8913

9014
return nil
9115
}

models/migrations/v208.go

Lines changed: 1 addition & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -5,47 +5,10 @@
55
package migrations
66

77
import (
8-
"encoding/base32"
9-
"encoding/base64"
10-
118
"xorm.io/xorm"
129
)
1310

1411
func useBase32HexForCredIDInWebAuthnCredential(x *xorm.Engine) error {
15-
16-
// Create webauthnCredential table
17-
type webauthnCredential struct {
18-
ID int64 `xorm:"pk autoincr"`
19-
CredentialID string `xorm:"INDEX VARCHAR(410)"`
20-
}
21-
if err := x.Sync2(&webauthnCredential{}); err != nil {
22-
return err
23-
}
24-
25-
var start int
26-
regs := make([]*webauthnCredential, 0, 50)
27-
for {
28-
err := x.OrderBy("id").Limit(50, start).Find(&regs)
29-
if err != nil {
30-
return err
31-
}
32-
33-
for _, reg := range regs {
34-
credID, _ := base64.RawStdEncoding.DecodeString(reg.CredentialID)
35-
reg.CredentialID = base32.HexEncoding.EncodeToString(credID)
36-
37-
_, err := x.Update(reg)
38-
if err != nil {
39-
return err
40-
}
41-
}
42-
43-
if len(regs) < 50 {
44-
break
45-
}
46-
start += 50
47-
regs = regs[:0]
48-
}
49-
12+
// noop
5013
return nil
5114
}

models/migrations/v209.go

Lines changed: 3 additions & 130 deletions
Original file line numberDiff line numberDiff line change
@@ -5,140 +5,13 @@
55
package migrations
66

77
import (
8-
"encoding/base32"
9-
"fmt"
10-
"strings"
11-
12-
"code.gitea.io/gitea/modules/timeutil"
13-
14-
"github.com/tstranex/u2f"
158
"xorm.io/xorm"
16-
"xorm.io/xorm/schemas"
179
)
1810

1911
func increaseCredentialIDTo410(x *xorm.Engine) error {
20-
// Create webauthnCredential table
21-
type webauthnCredential struct {
22-
ID int64 `xorm:"pk autoincr"`
23-
Name string
24-
LowerName string `xorm:"unique(s)"`
25-
UserID int64 `xorm:"INDEX unique(s)"`
26-
CredentialID string `xorm:"INDEX VARCHAR(410)"` // CredentalID in U2F is at most 255bytes / 5 * 8 = 408 - add a few extra characters for safety
27-
PublicKey []byte
28-
AttestationType string
29-
AAGUID []byte
30-
SignCount uint32 `xorm:"BIGINT"`
31-
CloneWarning bool
32-
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
33-
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
34-
}
35-
if err := x.Sync2(&webauthnCredential{}); err != nil {
36-
return err
37-
}
38-
39-
switch x.Dialect().URI().DBType {
40-
case schemas.MYSQL:
41-
_, err := x.Exec("ALTER TABLE webauthn_credential MODIFY COLUMN credential_id VARCHAR(410)")
42-
if err != nil {
43-
return err
44-
}
45-
case schemas.ORACLE:
46-
_, err := x.Exec("ALTER TABLE webauthn_credential MODIFY credential_id VARCHAR(410)")
47-
if err != nil {
48-
return err
49-
}
50-
case schemas.MSSQL:
51-
// This column has an index on it. I could write all of the code to attempt to change the index OR
52-
// I could just use recreate table.
53-
sess := x.NewSession()
54-
if err := sess.Begin(); err != nil {
55-
_ = sess.Close()
56-
return err
57-
}
58-
59-
if err := recreateTable(sess, new(webauthnCredential)); err != nil {
60-
_ = sess.Close()
61-
return err
62-
}
63-
if err := sess.Commit(); err != nil {
64-
_ = sess.Close()
65-
return err
66-
}
67-
if err := sess.Close(); err != nil {
68-
return err
69-
}
70-
case schemas.POSTGRES:
71-
_, err := x.Exec("ALTER TABLE webauthn_credential ALTER COLUMN credential_id TYPE VARCHAR(410)")
72-
if err != nil {
73-
return err
74-
}
75-
default:
76-
// SQLite doesn't support ALTER COLUMN, and it already makes String _TEXT_ by default so no migration needed
77-
// nor is there any need to re-migrate
78-
return nil
79-
}
80-
81-
exist, err := x.IsTableExist("u2f_registration")
82-
if err != nil {
83-
return err
84-
}
85-
if !exist {
86-
return nil
87-
}
88-
89-
// Now migrate the old u2f registrations to the new format
90-
type u2fRegistration struct {
91-
ID int64 `xorm:"pk autoincr"`
92-
Name string
93-
UserID int64 `xorm:"INDEX"`
94-
Raw []byte
95-
Counter uint32 `xorm:"BIGINT"`
96-
CreatedUnix timeutil.TimeStamp `xorm:"INDEX created"`
97-
UpdatedUnix timeutil.TimeStamp `xorm:"INDEX updated"`
98-
}
99-
100-
var start int
101-
regs := make([]*u2fRegistration, 0, 50)
102-
for {
103-
err := x.OrderBy("id").Limit(50, start).Find(&regs)
104-
if err != nil {
105-
return err
106-
}
107-
108-
for _, reg := range regs {
109-
parsed := new(u2f.Registration)
110-
err = parsed.UnmarshalBinary(reg.Raw)
111-
if err != nil {
112-
continue
113-
}
114-
115-
cred := &webauthnCredential{}
116-
has, err := x.ID(reg.ID).Where("id = ? AND user_id = ?", reg.ID, reg.UserID).Get(cred)
117-
if err != nil {
118-
return fmt.Errorf("unable to get webauthn_credential[%d]. Error: %v", reg.ID, err)
119-
}
120-
if !has {
121-
continue
122-
}
123-
remigratedCredID := base32.HexEncoding.EncodeToString(parsed.KeyHandle)
124-
if cred.CredentialID == remigratedCredID || (!strings.HasPrefix(remigratedCredID, cred.CredentialID) && cred.CredentialID != "") {
125-
continue
126-
}
127-
128-
cred.CredentialID = remigratedCredID
129-
130-
_, err = x.ID(cred.ID).Update(cred)
131-
if err != nil {
132-
return err
133-
}
134-
}
135-
136-
if len(regs) < 50 {
137-
break
138-
}
139-
start += 50
140-
regs = regs[:0]
141-
}
12+
// no-op
13+
// V208 is badly broken
14+
// So now we have to no-op again.
14215

14316
return nil
14417
}

0 commit comments

Comments
 (0)