-
Notifications
You must be signed in to change notification settings - Fork 432
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Revise LUKS API to expect key slot and salt when escrowing successfully #23952
base: main
Are you sure you want to change the base?
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
package tables | ||
|
||
import ( | ||
"database/sql" | ||
"fmt" | ||
) | ||
|
||
func init() { | ||
MigrationClient.AddMigration(Up_20241116233322, Down_20241116233322) | ||
} | ||
|
||
func Up_20241116233322(tx *sql.Tx) error { | ||
_, err := tx.Exec(`ALTER TABLE host_disk_encryption_keys | ||
ADD COLUMN base64_encrypted_salt VARCHAR(255) NOT NULL DEFAULT '' AFTER base64_encrypted, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Touché. Fixing. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Of note, we could save storage here by keeping the charset as-is, because the column should never be joined or sorted, and since this is base64'd even ASCII would represent this fine. But might as well be consistent. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What's the reason to use There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Slight cleanliness thing when I (or others) use the |
||
ADD COLUMN key_slot TINYINT UNSIGNED DEFAULT NULL AFTER base64_encrypted_salt`) | ||
if err != nil { | ||
return fmt.Errorf("failed to add base64_encrypted_salt and key_slot columns to host_disk_encryption_keys: %w", err) | ||
} | ||
|
||
return nil | ||
} | ||
|
||
func Down_20241116233322(tx *sql.Tx) error { | ||
return nil | ||
} |
This file was deleted.
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -1024,7 +1024,8 @@ func (svc *Service) SetOrUpdateDiskEncryptionKey(ctx context.Context, encryption | |
type orbitPostLUKSRequest struct { | ||
OrbitNodeKey string `json:"orbit_node_key"` | ||
Passphrase string `json:"passphrase"` | ||
SlotKey string `json:"slot_key"` | ||
Salt string `json:"salt"` | ||
KeySlot *uint `json:"key_slot"` | ||
ClientError string `json:"client_error"` | ||
} | ||
|
||
|
@@ -1047,13 +1048,13 @@ func (r orbitPostLUKSResponse) Status() int { return http.StatusNoContent } | |
|
||
func postOrbitLUKSEndpoint(ctx context.Context, request interface{}, svc fleet.Service) (errorer, error) { | ||
req := request.(*orbitPostLUKSRequest) | ||
if err := svc.EscrowLUKSData(ctx, req.Passphrase, req.SlotKey, req.ClientError); err != nil { | ||
if err := svc.EscrowLUKSData(ctx, req.Passphrase, req.Salt, req.KeySlot, req.ClientError); err != nil { | ||
return orbitPostLUKSResponse{Err: err}, nil | ||
} | ||
return orbitPostLUKSResponse{}, nil | ||
} | ||
|
||
func (svc *Service) EscrowLUKSData(ctx context.Context, passphrase string, slotKey string, clientError string) error { | ||
func (svc *Service) EscrowLUKSData(ctx context.Context, passphrase string, salt string, keySlot *uint, clientError string) error { | ||
// this is not a user-authenticated endpoint | ||
svc.authz.SkipAuthorization(ctx) | ||
|
||
|
@@ -1065,36 +1066,34 @@ func (svc *Service) EscrowLUKSData(ctx context.Context, passphrase string, slotK | |
if clientError != "" { | ||
return svc.ds.ReportEscrowError(ctx, host.ID, clientError) | ||
} | ||
encryptedPassphrase, encryptedSlotKey, err := svc.validateAndEncrypt(ctx, passphrase, slotKey) | ||
|
||
encryptedPassphrase, encryptedSalt, validatedKeySlot, err := svc.validateAndEncrypt(ctx, passphrase, salt, keySlot) | ||
if err != nil { | ||
_ = svc.ds.ReportEscrowError(ctx, host.ID, err.Error()) | ||
return err | ||
} | ||
|
||
return svc.ds.SaveLUKSData(ctx, host.ID, encryptedPassphrase, encryptedSlotKey) | ||
return svc.ds.SaveLUKSData(ctx, host.ID, encryptedPassphrase, encryptedSalt, validatedKeySlot) | ||
} | ||
|
||
func (svc *Service) validateAndEncrypt(ctx context.Context, passphrase string, slotKey string) (string, string, error) { | ||
if passphrase == "" { | ||
return "", "", badRequest("Blank passphrase provided") | ||
func (svc *Service) validateAndEncrypt(ctx context.Context, passphrase string, salt string, keySlot *uint) (string, string, uint, error) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nit, when having many same-typed return types, it's a good practice to use named returns: There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks for the call-out. Will fix. |
||
if passphrase == "" || salt == "" || keySlot == nil { | ||
return "", "", 0, badRequest("passphrase, salt, and key_slot must be provided to escrow LUKS data") | ||
} | ||
if svc.config.Server.PrivateKey == "" { | ||
return "", "", newOsqueryError("internal error: missing server private key") | ||
return "", "", 0, newOsqueryError("internal error: missing server private key") | ||
} | ||
|
||
encryptedPassphrase, err := mdm.EncryptAndEncode(passphrase, svc.config.Server.PrivateKey) | ||
if err != nil { | ||
return "", "", ctxerr.Wrap(ctx, err, "internal error: could not encrypt LUKS data") | ||
return "", "", 0, ctxerr.Wrap(ctx, err, "internal error: could not encrypt LUKS data") | ||
} | ||
var encryptedSlotKey string | ||
if slotKey != "" { | ||
encryptedSlotKey, err = mdm.EncryptAndEncode(slotKey, svc.config.Server.PrivateKey) | ||
if err != nil { | ||
return "", "", ctxerr.Wrap(ctx, err, "internal error: could not encrypt LUKS data") | ||
} | ||
encryptedSalt, err := mdm.EncryptAndEncode(salt, svc.config.Server.PrivateKey) | ||
if err != nil { | ||
return "", "", 0, ctxerr.Wrap(ctx, err, "internal error: could not encrypt LUKS data") | ||
} | ||
|
||
return encryptedPassphrase, encryptedSlotKey, nil | ||
return encryptedPassphrase, encryptedSalt, *keySlot, nil | ||
} | ||
|
||
///////////////////////////////////////////////////////////////////////////////// | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Should keySlot be
*uint
to map cleanly to the underlying NULLable type type.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nope. Only reason key slot is nullable in the DB is we use this table for BitLocker/FileVault, which (from our perspective at least) have neither key slot nor salt. Per the Slack convo earlier today, we're guaranteed to get key slot if we have valid LUKS data, and this method is only called when we have valid LUKS data, so we can tighten the parameter type.
Likewise we're guaranteed to have passphrase and salt when persisting LUKS, but I can't enforce that easily in the type system, hence the runtime check.