update deps; experiment: log security

This commit is contained in:
Aine
2022-11-16 23:00:58 +02:00
parent 225ba2ee9b
commit 99a89ef87a
55 changed files with 883 additions and 308 deletions

View File

@@ -10,18 +10,17 @@ import (
func (l *Linkpearl) GetAccountData(name string) (map[string]string, error) {
cached, ok := l.acc.Get(name)
if ok {
l.log.Debug("GetAccountData(%s) from cache (data): %+v", name, cached)
l.logAccountData(l.log.Debug, "GetAccountData(%q) cached:", cached, name)
if cached == nil {
return map[string]string{}, nil
}
return cached, nil
}
l.log.Debug("GetAccountData(%s) from API", name)
var data map[string]string
err := l.GetClient().GetAccountData(name, &data)
if err != nil {
l.log.Debug("GetAccountData(%s) from API (error): %v", name, err)
l.logAccountData(l.log.Debug, "GetAccountData(%q) error: %v", nil, name, err)
data = map[string]string{}
if strings.Contains(err.Error(), "M_NOT_FOUND") {
l.acc.Add(name, data)
@@ -30,7 +29,7 @@ func (l *Linkpearl) GetAccountData(name string) (map[string]string, error) {
return data, err
}
data = l.decryptAccountData(data)
l.log.Debug("GetAccountData(%s) from API (data): %+v", name, data)
l.logAccountData(l.log.Debug, "GetAccountData(%q):", data, name)
l.acc.Add(name, data)
return data, err
@@ -40,7 +39,7 @@ func (l *Linkpearl) GetAccountData(name string) (map[string]string, error) {
func (l *Linkpearl) SetAccountData(name string, data map[string]string) error {
l.acc.Add(name, data)
l.log.Debug("SetAccountData(%s) to API (data): %+v", name, data)
l.logAccountData(l.log.Debug, "SetAccountData(%q):", data, name)
data = l.encryptAccountData(data)
return l.GetClient().SetAccountData(name, data)
}
@@ -50,18 +49,17 @@ func (l *Linkpearl) GetRoomAccountData(roomID id.RoomID, name string) (map[strin
key := roomID.String() + name
cached, ok := l.acc.Get(key)
if ok {
l.log.Debug("GetRoomAccountData(%s, %s) from cache (data): %+v", roomID, name, cached)
l.logAccountData(l.log.Debug, "GetRoomAccountData(%q, %q) cached:", cached, roomID, name)
if cached == nil {
return map[string]string{}, nil
}
return cached, nil
}
l.log.Debug("GetRoomAccountData(%s, %s) from API", roomID, name)
var data map[string]string
err := l.GetClient().GetRoomAccountData(roomID, name, &data)
if err != nil {
l.log.Debug("GetRoomAccountData(%s, %s) from API (error): %v", roomID, name, err)
l.logAccountData(l.log.Debug, "GetRoomAccountData(%q, %q) error: %v", nil, roomID, name, err)
data = map[string]string{}
if strings.Contains(err.Error(), "M_NOT_FOUND") {
l.acc.Add(key, data)
@@ -70,7 +68,7 @@ func (l *Linkpearl) GetRoomAccountData(roomID id.RoomID, name string) (map[strin
return data, err
}
data = l.decryptAccountData(data)
l.log.Debug("GetRoomAccountData(%s,%s) from API (data): %+v", roomID, name, data)
l.logAccountData(l.log.Debug, "GetRoomAccountData(%q, %q):", data, roomID, name)
l.acc.Add(key, data)
return data, err
@@ -81,7 +79,7 @@ func (l *Linkpearl) SetRoomAccountData(roomID id.RoomID, name string, data map[s
key := roomID.String() + name
l.acc.Add(key, data)
l.log.Debug("SetRoomAccountData(%s, %s) to API (data): %+v", roomID, name, data)
l.logAccountData(l.log.Debug, "SetRoomAccountData(%q, %q):", data, roomID, name)
data = l.encryptAccountData(data)
return l.GetClient().SetRoomAccountData(roomID, name, data)
}
@@ -95,11 +93,11 @@ func (l *Linkpearl) encryptAccountData(data map[string]string) map[string]string
for k, v := range data {
ek, err := l.acr.Encrypt(k)
if err != nil {
l.log.Error("cannot encrypt account data (key=%s): %v", k, err)
l.log.Error("cannot encrypt account data (key=%q): %v", k, err)
}
ev, err := l.acr.Encrypt(v)
if err != nil {
l.log.Error("cannot encrypt account data (key=%s): %v", k, err)
l.log.Error("cannot encrypt account data (key=%q): %v", k, err)
}
encrypted[ek] = ev // worst case: plaintext value
}
@@ -116,14 +114,35 @@ func (l *Linkpearl) decryptAccountData(data map[string]string) map[string]string
for ek, ev := range data {
k, err := l.acr.Decrypt(ek)
if err != nil {
l.log.Error("cannot decrypt account data (key=%s): %v", k, err)
l.log.Error("cannot decrypt account data (key=%q): %v", k, err)
}
v, err := l.acr.Decrypt(ev)
if err != nil {
l.log.Error("cannot decrypt account data (key=%s): %v", k, err)
l.log.Error("cannot decrypt account data (key=%q): %v", k, err)
}
decrypted[k] = v // worst case: encrypted value, usual case: migration from plaintext to encrypted account data
}
return decrypted
}
func (l *Linkpearl) logAccountData(method func(string, ...any), message string, data map[string]string, args ...any) {
if len(data) == 0 {
method(message, args...)
return
}
safeData := make(map[string]string, len(data))
for k, v := range data {
sv, ok := l.aclr[k]
if ok {
safeData[k] = sv
continue
}
safeData[k] = v
}
args = append(args, safeData)
method(message+" %+v", args...)
}

View File

@@ -32,6 +32,11 @@ type Config struct {
// AccountDataSecret (Password) for encryption
AccountDataSecret string
// AccountDataLogReplace contains map of field name => value
// that will be used to replace mentioned account data fields with provided values
// when printing in logs (DEBUG, TRACE)
AccountDataLogReplace map[string]string
// MaxRetries for operations like auto join
MaxRetries int

View File

@@ -25,6 +25,7 @@ type Linkpearl struct {
db *sql.DB
acc *lru.Cache[string, map[string]string]
acr *Crypter
aclr map[string]string
log config.Logger
api *mautrix.Client
olm *crypto.OlmMachine
@@ -41,6 +42,9 @@ type ReqPresence struct {
}
func setDefaults(cfg *config.Config) {
if cfg.AccountDataLogReplace == nil {
cfg.AccountDataLogReplace = make(map[string]string)
}
if cfg.MaxRetries == 0 {
cfg.MaxRetries = DefaultMaxRetries
}
@@ -80,6 +84,7 @@ func New(cfg *config.Config) (*Linkpearl, error) {
db: cfg.DB,
acc: acc,
acr: acr,
aclr: cfg.AccountDataLogReplace,
api: api,
log: cfg.LPLogger,
joinPermit: cfg.JoinPermit,

View File

@@ -10,10 +10,10 @@ import (
// Send a message to the roomID and automatically try to encrypt it, if the destination room is encrypted
func (l *Linkpearl) Send(roomID id.RoomID, content interface{}) (id.EventID, error) {
if !l.store.IsEncrypted(roomID) {
l.log.Debug("room %s is not encrypted", roomID)
l.log.Debug("room %q is not encrypted", roomID)
return l.SendPlaintext(roomID, content)
}
l.log.Debug("room %s is encrypted", roomID)
l.log.Debug("room %q is encrypted", roomID)
encrypted, err := l.EncryptEvent(roomID, content)
if err != nil {
@@ -28,7 +28,7 @@ func (l *Linkpearl) Send(roomID id.RoomID, content interface{}) (id.EventID, err
func (l *Linkpearl) SendFile(roomID id.RoomID, req *mautrix.ReqUploadMedia, msgtype event.MessageType, relation *event.RelatesTo) error {
resp, err := l.GetClient().UploadMedia(*req)
if err != nil {
l.log.Error("cannot upload file %s: %v", req.FileName, err)
l.log.Error("cannot upload file %q: %v", req.FileName, err)
return err
}
_, err = l.Send(roomID, &event.Content{
@@ -40,7 +40,7 @@ func (l *Linkpearl) SendFile(roomID id.RoomID, req *mautrix.ReqUploadMedia, msgt
},
})
if err != nil {
l.log.Error("cannot send uploaded file: %s: %v", req.FileName, err)
l.log.Error("cannot send uploaded file: %q: %v", req.FileName, err)
}
return err
@@ -48,7 +48,7 @@ func (l *Linkpearl) SendFile(roomID id.RoomID, req *mautrix.ReqUploadMedia, msgt
// SendPlaintext sends plaintext event only
func (l *Linkpearl) SendPlaintext(roomID id.RoomID, content interface{}) (id.EventID, error) {
l.log.Debug("sending plaintext event to %s: %+v", roomID, content)
l.log.Debug("sending plaintext event to %q: %+v", roomID, content)
resp, err := l.api.SendMessageEvent(roomID, event.EventMessage, content)
if err != nil {
return "", err
@@ -58,7 +58,7 @@ func (l *Linkpearl) SendPlaintext(roomID id.RoomID, content interface{}) (id.Eve
// SendEncrypted sends encrypted event only
func (l *Linkpearl) SendEncrypted(roomID id.RoomID, content interface{}) (id.EventID, error) {
l.log.Debug("sending encrypted event to %s: %+v", roomID, content)
l.log.Debug("sending encrypted event to %q: %+v", roomID, content)
resp, err := l.api.SendMessageEvent(roomID, event.EventEncrypted, content)
if err != nil {
return "", err

View File

@@ -42,43 +42,43 @@ func (s *Store) GetAccount() (*crypto.OlmAccount, error) {
// HasSession returns whether there is an Olm session for the given sender key.
func (s *Store) HasSession(key id.SenderKey) bool {
s.log.Debug("check if olm session exists for the key %s", key)
s.log.Debug("check if olm session exists for the key %q", key)
return s.s.HasSession(key)
}
// GetSessions returns all the known Olm sessions for a sender key.
func (s *Store) GetSessions(key id.SenderKey) (crypto.OlmSessionList, error) {
s.log.Debug("loading olm session for the key %s", key)
s.log.Debug("loading olm session for the key %q", key)
return s.s.GetSessions(key)
}
// GetLatestSession retrieves the Olm session for a given sender key from the database that has the largest ID.
func (s *Store) GetLatestSession(key id.SenderKey) (*crypto.OlmSession, error) {
s.log.Debug("loading latest session for the key %s", key)
s.log.Debug("loading latest session for the key %q", key)
return s.s.GetLatestSession(key)
}
// AddSession persists an Olm session for a sender in the database.
func (s *Store) AddSession(key id.SenderKey, session *crypto.OlmSession) error {
s.log.Debug("adding new olm session for the key %s", key)
s.log.Debug("adding new olm session for the key %q", key)
return s.s.AddSession(key, session)
}
// UpdateSession replaces the Olm session for a sender in the database.
func (s *Store) UpdateSession(key id.SenderKey, session *crypto.OlmSession) error {
s.log.Debug("update olm session for the key %s", key)
s.log.Debug("update olm session for the key %q", key)
return s.s.UpdateSession(key, session)
}
// PutGroupSession stores an inbound Megolm group session for a room, sender and session.
func (s *Store) PutGroupSession(roomID id.RoomID, senderKey id.SenderKey, sessionID id.SessionID, session *crypto.InboundGroupSession) error {
s.log.Debug("storing inbound group session for the room %s", roomID)
s.log.Debug("storing inbound group session for the room %q", roomID)
return s.s.PutGroupSession(roomID, senderKey, sessionID, session)
}
// GetGroupSession retrieves an inbound Megolm group session for a room, sender and session.
func (s *Store) GetGroupSession(roomID id.RoomID, senderKey id.SenderKey, sessionID id.SessionID) (*crypto.InboundGroupSession, error) {
s.log.Debug("loading inbound group session for the room %s", roomID)
s.log.Debug("loading inbound group session for the room %q", roomID)
return s.s.GetGroupSession(roomID, senderKey, sessionID)
}
@@ -98,7 +98,7 @@ func (s *Store) GetWithheldGroupSession(roomID id.RoomID, senderKey id.SenderKey
// GetGroupSessionsForRoom gets all the inbound Megolm sessions for a specific room. This is used for creating key
// export files. Unlike GetGroupSession, this should not return any errors about withheld keys.
func (s *Store) GetGroupSessionsForRoom(roomID id.RoomID) ([]*crypto.InboundGroupSession, error) {
s.log.Debug("loading group session for the room %s", roomID)
s.log.Debug("loading group session for the room %q", roomID)
return s.s.GetGroupSessionsForRoom(roomID)
}
@@ -143,31 +143,31 @@ func (s *Store) ValidateMessageIndex(senderKey id.SenderKey, sessionID id.Sessio
// GetDevices returns a map of device IDs to device identities, including the identity and signing keys, for a given user ID.
func (s *Store) GetDevices(userID id.UserID) (map[id.DeviceID]*id.Device, error) {
s.log.Debug("loading devices of the %s", userID)
s.log.Debug("loading devices of the %q", userID)
return s.s.GetDevices(userID)
}
// GetDevice returns the device dentity for a given user and device ID.
func (s *Store) GetDevice(userID id.UserID, deviceID id.DeviceID) (*id.Device, error) {
s.log.Debug("loading device %s for the %s", deviceID, userID)
s.log.Debug("loading device %q for the %q", deviceID, userID)
return s.s.GetDevice(userID, deviceID)
}
// FindDeviceByKey finds a specific device by its sender key.
func (s *Store) FindDeviceByKey(userID id.UserID, identityKey id.IdentityKey) (*id.Device, error) {
s.log.Debug("loading device of the %s by the key %s", userID, identityKey)
s.log.Debug("loading device of the %q by the key %q", userID, identityKey)
return s.s.FindDeviceByKey(userID, identityKey)
}
// PutDevice stores a single device for a user, replacing it if it exists already.
func (s *Store) PutDevice(userID id.UserID, device *id.Device) error {
s.log.Debug("storing device of the %s", userID)
s.log.Debug("storing device of the %q", userID)
return s.s.PutDevice(userID, device)
}
// PutDevices stores the device identity information for the given user ID.
func (s *Store) PutDevices(userID id.UserID, devices map[id.DeviceID]*id.Device) error {
s.log.Debug("storing devices of the %s", userID)
s.log.Debug("storing devices of the %q", userID)
return s.s.PutDevices(userID, devices)
}
@@ -179,13 +179,13 @@ func (s *Store) FilterTrackedUsers(users []id.UserID) ([]id.UserID, error) {
// PutCrossSigningKey stores a cross-signing key of some user along with its usage.
func (s *Store) PutCrossSigningKey(userID id.UserID, usage id.CrossSigningUsage, key id.Ed25519) error {
s.log.Debug("storing crosssigning key of the %s", userID)
s.log.Debug("storing crosssigning key of the %q", userID)
return s.s.PutCrossSigningKey(userID, usage, key)
}
// GetCrossSigningKeys retrieves a user's stored cross-signing keys.
func (s *Store) GetCrossSigningKeys(userID id.UserID) (map[id.CrossSigningUsage]id.CrossSigningKey, error) {
s.log.Debug("loading crosssigning keys of the %s", userID)
s.log.Debug("loading crosssigning keys of the %q", userID)
return s.s.GetCrossSigningKeys(userID)
}
@@ -209,6 +209,6 @@ func (s *Store) IsKeySignedBy(userID id.UserID, key id.Ed25519, signerID id.User
// DropSignaturesByKey deletes the signatures made by the given user and key from the store. It returns the number of signatures deleted.
func (s *Store) DropSignaturesByKey(userID id.UserID, key id.Ed25519) (int64, error) {
s.log.Debug("removing signatures by the %s/%s", userID, key)
s.log.Debug("removing signatures by the %q/%q", userID, key)
return s.s.DropSignaturesByKey(userID, key)
}

View File

@@ -20,7 +20,7 @@ func (s *Store) IsEncrypted(roomID id.RoomID) bool {
return false
}
s.log.Debug("checking if room %s is encrypted", roomID)
s.log.Debug("checking if room %q is encrypted", roomID)
return s.GetEncryptionEvent(roomID) != nil
}
@@ -79,7 +79,7 @@ func (s *Store) SetEncryptionEvent(evt *event.Event) {
// SetMembership saves room members
func (s *Store) SetMembership(evt *event.Event) {
s.log.Debug("saving membership event for %s", evt.RoomID)
s.log.Debug("saving membership event for %q", evt.RoomID)
tx, err := s.db.Begin()
if err != nil {
s.log.Error("cannot begin transaction: %v", err)
@@ -127,7 +127,7 @@ func (s *Store) SetMembership(evt *event.Event) {
// GetRoomMembers ...
func (s *Store) GetRoomMembers(roomID id.RoomID) []id.UserID {
s.log.Debug("loading room members of %s", roomID)
s.log.Debug("loading room members of %q", roomID)
query := "SELECT user_id FROM room_members WHERE room_id = $1"
rows, err := s.db.Query(query, roomID)
users := make([]id.UserID, 0)
@@ -148,7 +148,7 @@ func (s *Store) GetRoomMembers(roomID id.RoomID) []id.UserID {
// SaveSession to DB
func (s *Store) SaveSession(userID id.UserID, deviceID id.DeviceID, accessToken string) {
s.log.Debug("saving session credentials of %s/%s", userID, deviceID)
s.log.Debug("saving session credentials of %q/%q", userID, deviceID)
tx, err := s.db.Begin()
if err != nil {
s.log.Error("cannot begin transaction: %v", err)

View File

@@ -16,7 +16,7 @@ func (s *Store) GetEncryptionEvent(roomID id.RoomID) *event.EncryptionEventConte
if !s.encryption {
return nil
}
s.log.Debug("finding encryption event of %s", roomID)
s.log.Debug("finding encryption event of %q", roomID)
query := "SELECT encryption_event FROM rooms WHERE room_id = $1"
row := s.db.QueryRow(query, roomID)
@@ -28,7 +28,7 @@ func (s *Store) GetEncryptionEvent(roomID id.RoomID) *event.EncryptionEventConte
}
var encryptionEvent event.EncryptionEventContent
if err := json.Unmarshal(encryptionEventJSON, &encryptionEvent); err != nil {
s.log.Debug("cannot unmarshal encryption event: %s", err)
s.log.Debug("cannot unmarshal encryption event: %q", err)
return nil
}
@@ -40,12 +40,12 @@ func (s *Store) FindSharedRooms(userID id.UserID) []id.RoomID {
if !s.encryption {
return nil
}
s.log.Debug("loading shared rooms for %s", userID)
s.log.Debug("loading shared rooms for %q", userID)
query := "SELECT room_id FROM room_members WHERE user_id = $1"
rows, queryErr := s.db.Query(query, userID)
rooms := make([]id.RoomID, 0)
if queryErr != nil {
s.log.Error("cannot load room members: %s", queryErr)
s.log.Error("cannot load room members: %q", queryErr)
return rooms
}
defer rows.Close()

View File

@@ -10,7 +10,7 @@ import (
// SaveFilterID to DB
func (s *Store) SaveFilterID(userID id.UserID, filterID string) {
s.log.Debug("saving filter ID %s for %s", filterID, userID)
s.log.Debug("saving filter ID %q for %q", filterID, userID)
tx, err := s.db.Begin()
if err != nil {
s.log.Error("cannot begin transaction: %v", err)
@@ -52,12 +52,12 @@ func (s *Store) SaveFilterID(userID id.UserID, filterID string) {
// LoadFilterID from DB
func (s *Store) LoadFilterID(userID id.UserID) string {
s.log.Debug("loading filter ID for %s", userID)
s.log.Debug("loading filter ID for %q", userID)
query := "SELECT filter_id FROM user_filter_ids WHERE user_id = $1"
row := s.db.QueryRow(query, userID)
var filterID string
if err := row.Scan(&filterID); err != nil {
s.log.Error("cannot load filter ID: %s", err)
s.log.Error("cannot load filter ID: %q", err)
return ""
}
return filterID
@@ -65,7 +65,7 @@ func (s *Store) LoadFilterID(userID id.UserID) string {
// SaveNextBatch to DB
func (s *Store) SaveNextBatch(userID id.UserID, nextBatchToken string) {
s.log.Debug("saving next batch token for %s", userID)
s.log.Debug("saving next batch token for %q", userID)
tx, err := s.db.Begin()
if err != nil {
s.log.Error("cannot begin transaction: %v", err)
@@ -103,7 +103,7 @@ func (s *Store) SaveNextBatch(userID id.UserID, nextBatchToken string) {
// LoadNextBatch from DB
func (s *Store) LoadNextBatch(userID id.UserID) string {
s.log.Debug("loading next batch token for %s", userID)
s.log.Debug("loading next batch token for %q", userID)
query := "SELECT next_batch_token FROM user_batch_tokens WHERE user_id = $1"
row := s.db.QueryRow(query, userID)
var batchToken string
@@ -116,11 +116,11 @@ func (s *Store) LoadNextBatch(userID id.UserID) string {
// SaveRoom to DB, not implemented
func (s *Store) SaveRoom(room *mautrix.Room) {
s.log.Debug("saving room %s (stub, not implemented)", room.ID)
s.log.Debug("saving room %q (stub, not implemented)", room.ID)
}
// LoadRoom from DB, not implemented
func (s *Store) LoadRoom(roomID id.RoomID) *mautrix.Room {
s.log.Debug("loading room %s (stub, not implemented)", roomID)
s.log.Debug("loading room %q (stub, not implemented)", roomID)
return mautrix.NewRoom(roomID)
}

View File

@@ -78,7 +78,7 @@ func (l *Linkpearl) tryJoin(roomID id.RoomID, retry int) {
_, err := l.api.JoinRoomByID(roomID)
if err != nil {
l.log.Error("cannot join the room %s: %v", roomID, err)
l.log.Error("cannot join the room %q: %v", roomID, err)
time.Sleep(5 * time.Second)
l.log.Debug("trying to join again (%d/%d)", retry+1, l.maxretries)
l.tryJoin(roomID, retry+1)