Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ai3
accountserver
Commits
ea481352
Commit
ea481352
authored
Apr 02, 2018
by
ale
Browse files
Initialize storage encryption on password change
parent
5c80803b
Changes
7
Hide whitespace changes
Inline
Side-by-side
actions.go
View file @
ea481352
...
...
@@ -33,6 +33,7 @@ type Backend interface {
SetResourcePassword
(
context
.
Context
,
string
,
*
Resource
,
string
)
error
GetUserEncryptionKeys
(
context
.
Context
,
*
User
)
([]
*
UserEncryptionKey
,
error
)
SetUserEncryptionKeys
(
context
.
Context
,
*
User
,
[]
*
UserEncryptionKey
)
error
SetUserEncryptionPublicKey
(
context
.
Context
,
*
User
,
[]
byte
)
error
SetApplicationSpecificPassword
(
context
.
Context
,
*
User
,
*
AppSpecificPasswordInfo
,
string
)
error
DeleteApplicationSpecificPassword
(
context
.
Context
,
*
User
,
string
)
error
}
...
...
@@ -171,12 +172,18 @@ func (s *AccountService) ChangeUserPassword(ctx context.Context, req *ChangeUser
return
err
}
if
err
:
=
req
.
Validate
();
err
!=
nil
{
if
err
=
req
.
Validate
();
err
!=
nil
{
return
newRequestError
(
err
)
}
if
err
:=
s
.
updateUserEncryptionKeys
(
ctx
,
user
,
req
.
CurPassword
,
req
.
Password
,
UserEncryptionKeyMainID
);
err
!=
nil
{
return
newBackendError
(
err
)
// If the user does not yet have an encryption key, generate one now.
if
!
user
.
HasEncryptionKeys
{
err
=
s
.
initializeUserEncryptionKeys
(
ctx
,
user
,
req
.
CurPassword
)
}
else
{
err
=
s
.
updateUserEncryptionKeys
(
ctx
,
user
,
req
.
CurPassword
,
req
.
Password
,
UserEncryptionKeyMainID
)
}
if
err
!=
nil
{
return
err
}
// Set the encrypted password attribute on the user and email resources.
...
...
@@ -193,8 +200,43 @@ func (s *AccountService) ChangeUserPassword(ctx context.Context, req *ChangeUser
return
nil
}
// Initialize the user encryption key list, by creating a new "main" key
// encrypted with the given password (which must be the primary password for the
// user).
func
(
s
*
AccountService
)
initializeUserEncryptionKeys
(
ctx
context
.
Context
,
user
*
User
,
curPassword
string
)
error
{
// Generate a new key pair.
pub
,
priv
,
err
:=
userenckey
.
GenerateKey
()
if
err
!=
nil
{
return
err
}
// Encrypt the private key with the password.
enc
,
err
:=
userenckey
.
Encrypt
(
priv
,
[]
byte
(
curPassword
))
if
err
!=
nil
{
return
err
}
keys
:=
[]
*
UserEncryptionKey
{
&
UserEncryptionKey
{
ID
:
UserEncryptionKeyMainID
,
Key
:
enc
,
},
}
// Update the backend database.
if
err
:=
s
.
backend
.
SetUserEncryptionKeys
(
ctx
,
user
,
keys
);
err
!=
nil
{
return
newBackendError
(
err
)
}
if
err
:=
s
.
backend
.
SetUserEncryptionPublicKey
(
ctx
,
user
,
pub
);
err
!=
nil
{
return
newBackendError
(
err
)
}
return
nil
}
// Re-encrypt the specified user encryption key with newPassword. For this
// operation to succeed, we must be able to decrypt one of the keys (not
// necessarily the same one) with curPassword.
func
(
s
*
AccountService
)
updateUserEncryptionKeys
(
ctx
context
.
Context
,
user
*
User
,
curPassword
,
newPassword
,
keyID
string
)
error
{
// Re-encrypt the user encryption key with the new password.
keys
,
err
:=
s
.
backend
.
GetUserEncryptionKeys
(
ctx
,
user
)
if
err
!=
nil
{
return
newBackendError
(
err
)
...
...
@@ -288,9 +330,11 @@ func (s *AccountService) CreateApplicationSpecificPassword(ctx context.Context,
// Create or update the user encryption key associated with
// this password. The user encryption key IDs for ASPs all
// have an 'asp_' prefix, followed by the ASP ID.
keyID
:=
"asp_"
+
asp
.
ID
if
err
:=
s
.
updateUserEncryptionKeys
(
ctx
,
user
,
req
.
CurPassword
,
password
,
keyID
);
err
!=
nil
{
return
nil
,
err
if
user
.
HasEncryptionKeys
{
keyID
:=
"asp_"
+
asp
.
ID
if
err
:=
s
.
updateUserEncryptionKeys
(
ctx
,
user
,
req
.
CurPassword
,
password
,
keyID
);
err
!=
nil
{
return
nil
,
err
}
}
return
&
CreateApplicationSpecificPasswordResponse
{
...
...
actions_test.go
View file @
ea481352
...
...
@@ -45,6 +45,10 @@ func (b *fakeBackend) SetUserEncryptionKeys(_ context.Context, user *User, keys
return
nil
}
func
(
b
*
fakeBackend
)
SetUserEncryptionPublicKey
(
_
context
.
Context
,
user
*
User
,
pub
[]
byte
)
error
{
return
nil
}
func
(
b
*
fakeBackend
)
SetApplicationSpecificPassword
(
_
context
.
Context
,
user
*
User
,
info
*
AppSpecificPasswordInfo
,
_
string
)
error
{
b
.
appSpecificPasswords
[
user
.
Name
]
=
append
(
b
.
appSpecificPasswords
[
user
.
Name
],
info
)
return
nil
...
...
@@ -90,6 +94,10 @@ func createFakeBackend() *fakeBackend {
},
},
},
resources
:
make
(
map
[
string
]
map
[
string
]
*
Resource
),
passwords
:
make
(
map
[
string
]
string
),
appSpecificPasswords
:
make
(
map
[
string
][]
*
AppSpecificPasswordInfo
),
encryptionKeys
:
make
(
map
[
string
][]
*
UserEncryptionKey
),
}
return
fb
}
...
...
@@ -159,7 +167,7 @@ func TestService_ChangePassword(t *testing.T) {
t
.
Fatal
(
err
)
}
if
fb
.
passwords
[
"testuser"
]
!
=
"password"
{
t
.
Fatal
f
(
"password was not set on the backend"
)
if
_
,
ok
:=
fb
.
passwords
[
"testuser"
]
;
!
ok
{
t
.
Fatal
(
"password was not set on the backend"
)
}
}
backend/model.go
View file @
ea481352
...
...
@@ -333,9 +333,10 @@ type ldapUserData struct {
func
newUser
(
entry
*
ldap
.
Entry
)
(
*
accountserver
.
User
,
error
)
{
user
:=
&
accountserver
.
User
{
Name
:
entry
.
GetAttributeValue
(
"uid"
),
Lang
:
entry
.
GetAttributeValue
(
"preferredLanguage"
),
Has2FA
:
(
entry
.
GetAttributeValue
(
"totpSecret"
)
!=
""
),
Name
:
entry
.
GetAttributeValue
(
"uid"
),
Lang
:
entry
.
GetAttributeValue
(
"preferredLanguage"
),
Has2FA
:
(
entry
.
GetAttributeValue
(
"totpSecret"
)
!=
""
),
HasEncryptionKeys
:
(
len
(
entry
.
GetAttributeValues
(
"storageEncryptionKey"
))
>
0
),
//PasswordRecoveryHint: entry.GetAttributeValue("recoverQuestion"),
}
if
user
.
Lang
==
""
{
...
...
@@ -459,6 +460,16 @@ func (b *LDAPBackend) SetUserEncryptionKeys(ctx context.Context, user *accountse
return
b
.
conn
.
Modify
(
ctx
,
mod
)
}
func
(
b
*
LDAPBackend
)
SetUserEncryptionPublicKey
(
ctx
context
.
Context
,
user
*
accountserver
.
User
,
pub
[]
byte
)
error
{
mod
:=
ldap
.
NewModifyRequest
(
getUserDN
(
user
))
if
user
.
HasEncryptionKeys
{
mod
.
Replace
(
"storageEncryptionPublicKey"
,
[]
string
{
string
(
pub
)})
}
else
{
mod
.
Add
(
"storageEncryptionPublicKey"
,
[]
string
{
string
(
pub
)})
}
return
b
.
conn
.
Modify
(
ctx
,
mod
)
}
func
(
b
*
LDAPBackend
)
SetApplicationSpecificPassword
(
ctx
context
.
Context
,
user
*
accountserver
.
User
,
info
*
accountserver
.
AppSpecificPasswordInfo
,
encryptedPassword
string
)
error
{
emailRsrc
:=
user
.
GetSingleResourceByType
(
accountserver
.
ResourceTypeEmail
)
if
emailRsrc
==
nil
{
...
...
vendor/git.autistici.org/id/keystore/userenckey/gen.go
0 → 100644
View file @
ea481352
package
userenckey
import
(
"crypto/ecdsa"
"crypto/elliptic"
"crypto/rand"
"crypto/x509"
"encoding/pem"
)
func
encodePublicKeyToPEM
(
pub
*
ecdsa
.
PublicKey
)
([]
byte
,
error
)
{
der
,
err
:=
x509
.
MarshalPKIXPublicKey
(
pub
)
if
err
!=
nil
{
return
nil
,
err
}
return
pem
.
EncodeToMemory
(
&
pem
.
Block
{
Type
:
"PUBLIC KEY"
,
Bytes
:
der
}),
nil
}
// GenerateKey generates a new ECDSA key pair, and returns the
// PEM-encoded public and private key (in order).
func
GenerateKey
()
([]
byte
,
[]
byte
,
error
)
{
pkey
,
err
:=
ecdsa
.
GenerateKey
(
elliptic
.
P224
(),
rand
.
Reader
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
privBytes
,
err
:=
encodePrivateKeyToPEM
(
pkey
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
pubBytes
,
err
:=
encodePublicKeyToPEM
(
&
pkey
.
PublicKey
)
if
err
!=
nil
{
return
nil
,
nil
,
err
}
return
pubBytes
,
privBytes
,
nil
}
vendor/git.autistici.org/id/keystore/userenckey/pkcs8.go
0 → 100644
View file @
ea481352
// +build go1.10
package
userenckey
import
(
"crypto/ecdsa"
"encoding/pem"
)
// Encode a private key to PEM-encoded PKCS8.
func
encodePrivateKeyToPEM
(
priv
*
ecdsa
.
PrivateKey
)
([]
byte
,
error
)
{
der
,
err
:=
x509
.
MarshalPKCS8PrivateKey
(
priv
)
if
err
!=
nil
{
return
nil
,
err
}
return
pem
.
EncodeToMemory
(
&
pem
.
Block
{
Type
:
"PRIVATE KEY"
,
Bytes
:
der
}),
nil
}
vendor/git.autistici.org/id/keystore/userenckey/pkcs8_compat.go
0 → 100644
View file @
ea481352
// +build !go1.10
package
userenckey
import
(
"bytes"
"crypto/ecdsa"
"crypto/x509"
"encoding/pem"
"os/exec"
)
// Encode a private key to PEM-encoded PKCS8.
//
// In Go versions prior to 1.10, we must shell out to openssl to
// convert the private key to PKCS8 format.
func
encodePrivateKeyToPEM
(
priv
*
ecdsa
.
PrivateKey
)
([]
byte
,
error
)
{
der
,
err
:=
x509
.
MarshalECPrivateKey
(
priv
)
if
err
!=
nil
{
return
nil
,
err
}
pkcs1
:=
pem
.
EncodeToMemory
(
&
pem
.
Block
{
Type
:
"EC PRIVATE KEY"
,
Bytes
:
der
})
cmd
:=
exec
.
Command
(
"/usr/bin/openssl"
,
"pkey"
)
cmd
.
Stdin
=
bytes
.
NewReader
(
pkcs1
)
return
cmd
.
Output
()
}
vendor/vendor.json
View file @
ea481352
...
...
@@ -39,10 +39,10 @@
"revisionTime"
:
"2018-02-18T15:46:43Z"
},
{
"checksumSHA1"
:
"
hNMdjPPJ99kTEVdtM8oQvx/FoWY
="
,
"checksumSHA1"
:
"
rPaBG1IesiEZi/dPae7wvsWtYdc
="
,
"path"
:
"git.autistici.org/id/keystore/userenckey"
,
"revision"
:
"
128c384b1908f52c7425367d8c9579d50c63
9c7
7
"
,
"revisionTime"
:
"2018-0
2-16T21:12:09
Z"
"revision"
:
"
f086b1b88dfbd16a2a4f68239d08f1195f4d
9c7
2
"
,
"revisionTime"
:
"2018-0
4-02T09:08:18
Z"
},
{
"checksumSHA1"
:
"yReqUM4tQkY+1YEI+L2d0SOzFWs="
,
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment