diff --git a/auth_requestor_auto.go b/auth_requestor_auto.go
index 7f41a3d2..6179d9d4 100644
--- a/auth_requestor_auto.go
+++ b/auth_requestor_auto.go
@@ -27,8 +27,9 @@ import (
)
var (
- newPlymouthAuthRequestor = NewPlymouthAuthRequestor
- newSystemdAuthRequestor = NewSystemdAuthRequestor
+ newPlymouthAuthRequestor = NewPlymouthAuthRequestor
+ newSystemdAuthRequestor = NewSystemdAuthRequestor
+ newSystemdCredsAuthRequestor = NewSystemdCredsAuthRequestor
)
type autoAuthRequestor struct {
@@ -59,6 +60,7 @@ func (r *autoAuthRequestor) NotifyUserAuthResult(ctx context.Context, result Use
// NewAutoAuthRequestor creates an implementation of AuthRequestor that automatically
// selects the first available implementation in the following order:
+// - systemd credential.
// - Plymouth.
// - systemd-ask-password.
//
@@ -71,9 +73,20 @@ func (r *autoAuthRequestor) NotifyUserAuthResult(ctx context.Context, result Use
// The caller supplies an implementation of AuthRequestorStringer that returns messages.
// The console argument is used by the systemd-ask-password implementation of
// [AuthRequestor.NotifyUserAuthResult] where result is not [UserAuthResultSuccess]. If not
-// provided, it defaults to [os.Stderr].
-func NewAutoAuthRequestor(stderr io.Writer, stringer AuthRequestorStringer) (AuthRequestor, error) {
+// provided, it defaults to [os.Stderr]. The credPrefix argument is used to specify the
+// prefix for systemd credentials.
+func NewAutoAuthRequestor(stderr io.Writer, stringer AuthRequestorStringer, credPrefix string) (AuthRequestor, error) {
var requestors []AuthRequestor
+
+ switch sdcred, err := newSystemdCredsAuthRequestor(stderr, credPrefix); {
+ case errors.Is(err, ErrAuthRequestorNotAvailable):
+ // ignore
+ case err != nil:
+ return nil, fmt.Errorf("cannot create sdcreds AuthRequestor: %w", err)
+ default:
+ requestors = append(requestors, sdcred)
+ }
+
switch ply, err := newPlymouthAuthRequestor(stringer); {
case errors.Is(err, ErrAuthRequestorNotAvailable):
// ignore
diff --git a/auth_requestor_auto_test.go b/auth_requestor_auto_test.go
index c7fbd22e..a5b6bbf7 100644
--- a/auth_requestor_auto_test.go
+++ b/auth_requestor_auto_test.go
@@ -25,6 +25,7 @@ import (
"errors"
"fmt"
"io"
+ "os"
"strings"
. "github.com/snapcore/secboot"
@@ -36,11 +37,13 @@ import (
type authRequestorAutoSuite struct {
snapd_testutil.BaseTest
+ authRequestorSdCredsTestMixin
authRequestorPlymouthTestMixin
authRequestorSystemdTestMixin
}
func (s *authRequestorAutoSuite) SetUpTest(c *C) {
+ s.AddCleanup(s.authRequestorSdCredsTestMixin.setUpTest(c))
s.AddCleanup(s.authRequestorPlymouthTestMixin.setUpTest(c))
s.AddCleanup(s.authRequestorSystemdTestMixin.setUpTest(c))
}
@@ -170,7 +173,22 @@ func (s *authRequestorAutoSuite) TestNewAuthRequestor(c *C) {
})
defer restore()
- requestor, err := NewAutoAuthRequestor(sdConsole, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(sdConsole, new(mockAutoAuthRequestorStringer), "")
+ c.Assert(err, IsNil)
+ c.Assert(requestor, NotNil)
+ c.Assert(requestor, testutil.ConvertibleTo, &AutoAuthRequestor{})
+ c.Assert(requestor.(*AutoAuthRequestor).Requestors(), HasLen, 3)
+ c.Check(requestor.(*AutoAuthRequestor).Requestors()[0], testutil.ConvertibleTo, &SystemdCredsAuthRequestor{})
+ c.Check(requestor.(*AutoAuthRequestor).Requestors()[1], testutil.ConvertibleTo, &PlymouthAuthRequestor{})
+ c.Check(requestor.(*AutoAuthRequestor).Requestors()[2], testutil.ConvertibleTo, &SystemdAuthRequestor{})
+}
+
+func (s *authRequestorAutoSuite) TestNewAuthRequestorSdCredsNotAvailable(c *C) {
+ sdConsole := new(bytes.Buffer)
+
+ c.Assert(os.Unsetenv("CREDENTIALS_DIRECTORY"), IsNil)
+
+ requestor, err := NewAutoAuthRequestor(sdConsole, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
c.Assert(requestor, NotNil)
c.Assert(requestor, testutil.ConvertibleTo, &AutoAuthRequestor{})
@@ -185,12 +203,13 @@ func (s *authRequestorAutoSuite) TestNewAuthRequestorPlymouthNotAvailable(c *C)
})
defer restore()
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
c.Assert(requestor, NotNil)
c.Assert(requestor, testutil.ConvertibleTo, &AutoAuthRequestor{})
- c.Assert(requestor.(*AutoAuthRequestor).Requestors(), HasLen, 1)
- c.Check(requestor.(*AutoAuthRequestor).Requestors()[0], testutil.ConvertibleTo, &SystemdAuthRequestor{})
+ c.Assert(requestor.(*AutoAuthRequestor).Requestors(), HasLen, 2)
+ c.Check(requestor.(*AutoAuthRequestor).Requestors()[0], testutil.ConvertibleTo, &SystemdCredsAuthRequestor{})
+ c.Check(requestor.(*AutoAuthRequestor).Requestors()[1], testutil.ConvertibleTo, &SystemdAuthRequestor{})
}
func (s *authRequestorAutoSuite) TestNewAuthRequestorSystemdNotAvailable(c *C) {
@@ -199,15 +218,18 @@ func (s *authRequestorAutoSuite) TestNewAuthRequestorSystemdNotAvailable(c *C) {
})
defer restore()
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
c.Assert(requestor, NotNil)
c.Assert(requestor, testutil.ConvertibleTo, &AutoAuthRequestor{})
- c.Assert(requestor.(*AutoAuthRequestor).Requestors(), HasLen, 1)
- c.Check(requestor.(*AutoAuthRequestor).Requestors()[0], testutil.ConvertibleTo, &PlymouthAuthRequestor{})
+ c.Assert(requestor.(*AutoAuthRequestor).Requestors(), HasLen, 2)
+ c.Check(requestor.(*AutoAuthRequestor).Requestors()[0], testutil.ConvertibleTo, &SystemdCredsAuthRequestor{})
+ c.Check(requestor.(*AutoAuthRequestor).Requestors()[1], testutil.ConvertibleTo, &PlymouthAuthRequestor{})
}
func (s *authRequestorAutoSuite) TestNewAuthRequestorNotAvailable(c *C) {
+ c.Assert(os.Unsetenv("CREDENTIALS_DIRECTORY"), IsNil)
+
restore := MockNewPlymouthAuthRequestor(func(_ AuthRequestorStringer) (AuthRequestor, error) {
return nil, ErrAuthRequestorNotAvailable
})
@@ -218,13 +240,13 @@ func (s *authRequestorAutoSuite) TestNewAuthRequestorNotAvailable(c *C) {
})
defer restore()
- _, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ _, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Check(err, ErrorMatches, "the auth requestor is not available")
c.Check(errors.Is(err, ErrAuthRequestorNotAvailable), testutil.IsTrue)
}
func (s *authRequestorAutoSuite) TestNewAuthRequestorPlymouthError(c *C) {
- _, err := NewAutoAuthRequestor(nil, nil)
+ _, err := NewAutoAuthRequestor(nil, nil, "")
c.Check(err, ErrorMatches, "cannot create Plymouth AuthRequestor: must supply an implementation of AuthRequestorStringer")
}
@@ -234,15 +256,35 @@ func (s *authRequestorAutoSuite) TestNewAuthRequestorSystemdError(c *C) {
})
defer restore()
- _, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ _, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Check(err, ErrorMatches, "cannot create systemd AuthRequestor: some error")
}
+func (s *authRequestorAutoSuite) TestRequestUserCredentialSdCred(c *C) {
+ // Ensure that a systemd cred is used first if available.
+ s.setPassphrase(c, "password")
+ s.writeCred(c, "foo", "/dev/sda1", "password", "passphrase")
+
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "foo")
+ c.Assert(err, IsNil)
+
+ passphrase, passphraseType, err := requestor.RequestUserCredential(context.Background(), "data", "/dev/sda1", UserAuthTypePassphrase)
+ c.Check(err, IsNil)
+ c.Check(passphrase, Equals, "password")
+ c.Check(passphraseType, Equals, UserAuthTypePassphrase)
+
+ c.Check(s.mockPlymouth.Calls(), HasLen, 0)
+ c.Check(s.mockSdAskPassword.Calls(), HasLen, 0)
+
+ c.Assert(requestor, testutil.ConvertibleTo, &AutoAuthRequestor{})
+ c.Check(requestor.(*AutoAuthRequestor).LastUsed(), testutil.ConvertibleTo, &SystemdCredsAuthRequestor{})
+}
+
func (s *authRequestorAutoSuite) TestRequestUserCredentialPlymouth(c *C) {
- // Ensure that plymouth is used first if available.
+ // Ensure that plymouth is used first if available and there is no systemd cred.
s.setPassphrase(c, "password")
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
passphrase, passphraseType, err := requestor.RequestUserCredential(context.Background(), "data", "/dev/sda1", UserAuthTypePassphrase)
@@ -265,7 +307,7 @@ func (s *authRequestorAutoSuite) TestRequestUserCredentialSystemd(c *C) {
s.setPassphrase(c, "password")
s.stopPlymouthd(c)
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
passphrase, passphraseType, err := requestor.RequestUserCredential(context.Background(), "data", "/dev/sda1", UserAuthTypePassphrase)
@@ -283,7 +325,7 @@ func (s *authRequestorAutoSuite) TestRequestUserCredentialSystemd(c *C) {
func (s *authRequestorAutoSuite) TestRequestUserCredentialDifferentName(c *C) {
s.setPassphrase(c, "password")
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
passphrase, passphraseType, err := requestor.RequestUserCredential(context.Background(), "foo", "/dev/sda1", UserAuthTypePassphrase)
@@ -304,7 +346,7 @@ func (s *authRequestorAutoSuite) TestRequestUserCredentialDifferentName(c *C) {
func (s *authRequestorAutoSuite) TestRequestUserCredentialDifferentPath(c *C) {
s.setPassphrase(c, "password")
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
passphrase, passphraseType, err := requestor.RequestUserCredential(context.Background(), "data", "/dev/nvme0n1p3", UserAuthTypePassphrase)
@@ -325,7 +367,7 @@ func (s *authRequestorAutoSuite) TestRequestUserCredentialDifferentPath(c *C) {
func (s *authRequestorAutoSuite) TestRequestUserCredentialDifferentCredentialType(c *C) {
s.setPassphrase(c, "password")
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
passphrase, passphraseType, err := requestor.RequestUserCredential(context.Background(), "foo", "/dev/sda1", UserAuthTypePassphrase|UserAuthTypeRecoveryKey)
@@ -346,7 +388,7 @@ func (s *authRequestorAutoSuite) TestRequestUserCredentialDifferentCredentialTyp
func (s *authRequestorAutoSuite) TestRequestUserCredentialDifferentPassphrase(c *C) {
s.setPassphrase(c, "1234")
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
passphrase, passphraseType, err := requestor.RequestUserCredential(context.Background(), "data", "/dev/sda1", UserAuthTypePassphrase)
@@ -367,7 +409,7 @@ func (s *authRequestorAutoSuite) TestRequestUserCredentialDifferentPassphrase(c
func (s *authRequestorAutoSuite) TestRequestUserCredentialCanceledContext(c *C) {
s.setPassphrase(c, "password")
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
ctx, cancel := context.WithCancel(context.Background())
@@ -382,7 +424,7 @@ func (s *authRequestorAutoSuite) TestRequestUserCredentialPlymouthFails(c *C) {
// Ensure we get an error if any implementation fails with an unexpected error.
s.authRequestorSystemdTestMixin.setPassphrase(c, "password")
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
_, _, err = requestor.RequestUserCredential(context.Background(), "data", "/dev/sda1", UserAuthTypePassphrase)
@@ -398,7 +440,7 @@ func (s *authRequestorAutoSuite) TestRequestUserCredentialNotAvailable(c *C) {
})
defer restore()
- requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer))
+ requestor, err := NewAutoAuthRequestor(nil, new(mockAutoAuthRequestorStringer), "")
c.Assert(err, IsNil)
_, _, err = requestor.RequestUserCredential(context.Background(), "data", "/dev/sda1", UserAuthTypePassphrase)
diff --git a/auth_requestor_plymouth_test.go b/auth_requestor_plymouth_test.go
index 60b323b9..95108542 100644
--- a/auth_requestor_plymouth_test.go
+++ b/auth_requestor_plymouth_test.go
@@ -83,6 +83,8 @@ type authRequestorPlymouthSuite struct {
}
func (s *authRequestorPlymouthSuite) SetUpTest(c *C) {
+ s.BaseTest.SetUpTest(c)
+
s.AddCleanup(s.authRequestorPlymouthTestMixin.setUpTest(c))
}
diff --git a/auth_requestor_sdcreds.go b/auth_requestor_sdcreds.go
new file mode 100644
index 00000000..a056ccb6
--- /dev/null
+++ b/auth_requestor_sdcreds.go
@@ -0,0 +1,124 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2026 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package secboot
+
+import (
+ "context"
+ "errors"
+ "fmt"
+ "io"
+ "os"
+ "path/filepath"
+ "strings"
+)
+
+type systemdCredsRequestUserCredentialContext struct {
+ Path string
+ CredPath string
+}
+
+type systemdCredsAuthRequestor struct {
+ console io.Writer
+ prefix string
+ credsDir string
+
+ lastRequestUserCredentialCtx systemdCredsRequestUserCredentialContext
+}
+
+func (r *systemdCredsAuthRequestor) RequestUserCredential(ctx context.Context, name, path string, authTypes UserAuthType) (string, UserAuthType, error) {
+ pathStr := strings.ReplaceAll(strings.TrimPrefix(path, "/"), "/", "-")
+
+ for _, info := range []struct {
+ authTypeStr string
+ authType UserAuthType
+ }{
+ {authTypeStr: "passphrase", authType: UserAuthTypePassphrase},
+ {authTypeStr: "pin", authType: UserAuthTypePIN},
+ {authTypeStr: "recoverykey", authType: UserAuthTypeRecoveryKey},
+ } {
+ if info.authType&authTypes == 0 {
+ continue
+ }
+
+ credPath := filepath.Join(r.credsDir, fmt.Sprintf("%s.%s.%s", r.prefix, pathStr, info.authTypeStr))
+ data, err := os.ReadFile(credPath)
+ if errors.Is(err, os.ErrNotExist) {
+ continue
+ }
+ if err != nil {
+ return "", 0, fmt.Errorf("cannot read credential from %s: %w", path, err)
+ }
+
+ r.lastRequestUserCredentialCtx = systemdCredsRequestUserCredentialContext{
+ Path: path,
+ CredPath: credPath,
+ }
+ return string(data), info.authType, nil
+ }
+
+ return "", 0, ErrAuthRequestorNotAvailable
+}
+
+func (r *systemdCredsAuthRequestor) NotifyUserAuthResult(ctx context.Context, result UserAuthResult, authTypes, exhaustedAuthTypes UserAuthType) error {
+ switch result {
+ case UserAuthResultFailed:
+ fmt.Fprintf(r.console, "Incorrect %s from credential %s for %s\n", formatUserAuthTypeString(authTypes), r.lastRequestUserCredentialCtx.CredPath, r.lastRequestUserCredentialCtx.Path)
+ case UserAuthResultInvalidFormat:
+ fmt.Fprintf(r.console, "Incorrectly formatted %s from credential %s\n", formatUserAuthTypeString(authTypes), r.lastRequestUserCredentialCtx.CredPath)
+ }
+
+ r.lastRequestUserCredentialCtx = systemdCredsRequestUserCredentialContext{}
+ return nil
+}
+
+// NewSystemdCredsAuthRequestor creates an implementation of AuthRequestor that
+// returns credentials from systemd credentials. The console argument is used by
+// the implementation of [AuthRequestor.NotifyUserAuthResult] where result is
+// not [UserAuthResultSuccess]. If not provided, it defaults to [os.Stderr].
+// The prefix argument can be used to customize the prefix used for looking up
+// credentials. It defaults to "ubuntu-fde" if not provided.
+//
+// Credentials can be provided by using the following format for their name:
+// ..
+// ... where is the path with the leading separator removed and remaining
+// separators replaced with '-', and is one of "passphrase", "pin" or
+// "recoverykey".
+//
+// This will return [ErrAuthRequestorNotAvailable] if the CREDENTIALS_DIRECTORY
+// environment variable is not set.
+func NewSystemdCredsAuthRequestor(console io.Writer, prefix string) (AuthRequestor, error) {
+ dir, exists := os.LookupEnv("CREDENTIALS_DIRECTORY")
+ if !exists {
+ return nil, ErrAuthRequestorNotAvailable
+ }
+
+ if console == nil {
+ console = os.Stderr
+ }
+ if prefix == "" {
+ prefix = "ubuntu-fde"
+ }
+
+ return &systemdCredsAuthRequestor{
+ console: console,
+ prefix: prefix,
+ credsDir: dir,
+ }, nil
+}
diff --git a/auth_requestor_sdcreds_test.go b/auth_requestor_sdcreds_test.go
new file mode 100644
index 00000000..aab2d50d
--- /dev/null
+++ b/auth_requestor_sdcreds_test.go
@@ -0,0 +1,312 @@
+// -*- Mode: Go; indent-tabs-mode: t -*-
+
+/*
+ * Copyright (C) 2026 Canonical Ltd
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 3 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ *
+ */
+
+package secboot_test
+
+import (
+ "bytes"
+ "context"
+ "errors"
+ "fmt"
+ "os"
+ "path/filepath"
+ "strings"
+
+ . "gopkg.in/check.v1"
+
+ . "github.com/snapcore/secboot"
+ "github.com/snapcore/secboot/internal/testutil"
+ snapd_testutil "github.com/snapcore/snapd/testutil"
+)
+
+type authRequestorSdCredsTestMixin struct {
+ credsDir string
+}
+
+func (m *authRequestorSdCredsTestMixin) setUpTest(c *C) (restore func()) {
+ m.credsDir = c.MkDir()
+
+ name := "CREDENTIALS_DIRECTORY"
+ orig, set := os.LookupEnv(name)
+ c.Assert(os.Setenv(name, m.credsDir), IsNil)
+ return func() {
+ if !set {
+ return
+ }
+ c.Assert(os.Setenv(name, orig), IsNil)
+ }
+}
+
+func (m *authRequestorSdCredsTestMixin) writeCred(c *C, prefix, path, cred, credType string) string {
+ name := filepath.Join(m.credsDir, fmt.Sprintf("%s.%s.%s", prefix, strings.ReplaceAll(path[1:], "/", "-"), credType))
+ c.Assert(os.WriteFile(name, []byte(cred), 0644), IsNil)
+ return name
+}
+
+type authRequestorSdCredsSuite struct {
+ snapd_testutil.BaseTest
+ authRequestorSdCredsTestMixin
+}
+
+func (s *authRequestorSdCredsSuite) SetUpTest(c *C) {
+ s.BaseTest.SetUpTest(c)
+
+ s.AddCleanup(s.authRequestorSdCredsTestMixin.setUpTest(c))
+}
+
+var _ = Suite(&authRequestorSdCredsSuite{})
+
+type testSdCredsRequestUserCredentialParams struct {
+ passphrase string
+ passphraseType string
+
+ prefix string
+
+ path string
+ authTypes UserAuthType
+
+ expectedAuthType UserAuthType
+}
+
+func (s *authRequestorSdCredsSuite) testRequestUserCredential(c *C, params *testSdCredsRequestUserCredentialParams) {
+ prefix := params.prefix
+ if prefix == "" {
+ prefix = "ubuntu-fde"
+ }
+ credPath := s.writeCred(c, prefix, params.path, params.passphrase, params.passphraseType)
+
+ requestor, err := NewSystemdCredsAuthRequestor(nil, params.prefix)
+ c.Assert(err, IsNil)
+
+ passphrase, passphraseType, err := requestor.RequestUserCredential(context.Background(), "data", params.path, params.authTypes)
+ c.Check(err, IsNil)
+ c.Check(passphrase, Equals, params.passphrase)
+ c.Check(passphraseType, Equals, params.expectedAuthType)
+
+ c.Assert(requestor, testutil.ConvertibleTo, &SystemdCredsAuthRequestor{})
+ c.Check(requestor.(*SystemdCredsAuthRequestor).LastRequestUserCredentialCtx(), DeepEquals, SystemdCredsRequestUserCredentialContext{
+ Path: params.path,
+ CredPath: credPath,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredential(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "password",
+ passphraseType: "passphrase",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypePassphrase,
+ expectedAuthType: UserAuthTypePassphrase,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialDifferentPassphrase(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "1234",
+ passphraseType: "passphrase",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypePassphrase,
+ expectedAuthType: UserAuthTypePassphrase,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialDifferentPath(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "password",
+ passphraseType: "passphrase",
+ path: "/dev/nvme0n1p3",
+ authTypes: UserAuthTypePassphrase,
+ expectedAuthType: UserAuthTypePassphrase,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialPIN(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "1234",
+ passphraseType: "pin",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypePIN,
+ expectedAuthType: UserAuthTypePIN,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialRecoveryKey(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "00000-11111-22222-33333-44444-55555-00000-11111",
+ passphraseType: "recoverykey",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypeRecoveryKey,
+ expectedAuthType: UserAuthTypeRecoveryKey,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialPassphraseOrPIN(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "1234",
+ passphraseType: "pin",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypePassphrase | UserAuthTypePIN,
+ expectedAuthType: UserAuthTypePIN,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialPassphraseOrRecoveryKey(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "password",
+ passphraseType: "passphrase",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypePassphrase | UserAuthTypeRecoveryKey,
+ expectedAuthType: UserAuthTypePassphrase,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialPINOrRecoveryKey(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "1234",
+ passphraseType: "pin",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypePIN | UserAuthTypeRecoveryKey,
+ expectedAuthType: UserAuthTypePIN,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialPassphraseOrPINOrRecoveryKey(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "password",
+ passphraseType: "passphrase",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypePassphrase | UserAuthTypePIN | UserAuthTypeRecoveryKey,
+ expectedAuthType: UserAuthTypePassphrase,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialDifferentPrefix(c *C) {
+ s.testRequestUserCredential(c, &testSdCredsRequestUserCredentialParams{
+ passphrase: "password",
+ passphraseType: "passphrase",
+ prefix: "foo",
+ path: "/dev/sda1",
+ authTypes: UserAuthTypePassphrase,
+ expectedAuthType: UserAuthTypePassphrase,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestNewRequestorNotAvailable(c *C) {
+ c.Assert(os.Unsetenv("CREDENTIALS_DIRECTORY"), IsNil)
+
+ _, err := NewSystemdCredsAuthRequestor(nil, "")
+ c.Check(err, ErrorMatches, `the auth requestor is not available`)
+ c.Check(errors.Is(err, ErrAuthRequestorNotAvailable), testutil.IsTrue)
+}
+
+func (s *authRequestorSdCredsSuite) TestRequestUserCredentialNotAvailable(c *C) {
+ s.writeCred(c, "ubuntu-fde", "/dev/sda1", "passphrase", "passphrase")
+
+ requestor, err := NewSystemdCredsAuthRequestor(nil, "")
+ c.Assert(err, IsNil)
+
+ _, _, err = requestor.RequestUserCredential(context.Background(), "data", "/dev/sda1", UserAuthTypePIN|UserAuthTypeRecoveryKey)
+ c.Check(err, ErrorMatches, `the auth requestor is not available`)
+ c.Check(errors.Is(err, ErrAuthRequestorNotAvailable), testutil.IsTrue)
+
+ c.Assert(requestor, testutil.ConvertibleTo, &SystemdCredsAuthRequestor{})
+ c.Check(requestor.(*SystemdCredsAuthRequestor).LastRequestUserCredentialCtx(), DeepEquals, SystemdCredsRequestUserCredentialContext{})
+}
+
+type testSdCredsNotifyUserAuthResultParams struct {
+ path string
+ credPath string
+ result UserAuthResult
+ authTypes UserAuthType
+ unavailableAuthTypes UserAuthType
+
+ expectedMsg string
+}
+
+func (s *authRequestorSdCredsSuite) testNotifyUserAuthResult(c *C, params *testSdCredsNotifyUserAuthResultParams) {
+ console := new(bytes.Buffer)
+ requestor := NewSystemdCredsAuthRequestorForTesting(console, "", "", &SystemdCredsRequestUserCredentialContext{
+ CredPath: params.credPath,
+ Path: params.path,
+ })
+
+ c.Check(requestor.NotifyUserAuthResult(nil, params.result, params.authTypes, params.unavailableAuthTypes), IsNil)
+ c.Check(console.String(), Equals, params.expectedMsg)
+}
+
+func (s *authRequestorSdCredsSuite) TestNotifyUserAuthResultSuccess(c *C) {
+ s.testNotifyUserAuthResult(c, &testSdCredsNotifyUserAuthResultParams{
+ result: UserAuthResultSuccess,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestNotifyUserAuthResultFailurePassphrase(c *C) {
+ s.testNotifyUserAuthResult(c, &testSdCredsNotifyUserAuthResultParams{
+ path: "/dev/sda1",
+ credPath: "/foo/ubuntu-fde.dev-sda1.passphrase",
+ result: UserAuthResultFailed,
+ authTypes: UserAuthTypePassphrase,
+ expectedMsg: `Incorrect passphrase from credential /foo/ubuntu-fde.dev-sda1.passphrase for /dev/sda1
+`,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestNotifyUserAuthResultFailurePIN(c *C) {
+ s.testNotifyUserAuthResult(c, &testSdCredsNotifyUserAuthResultParams{
+ path: "/dev/sda1",
+ credPath: "/foo/ubuntu-fde.dev-sda1.pin",
+ result: UserAuthResultFailed,
+ authTypes: UserAuthTypePIN,
+ expectedMsg: `Incorrect PIN from credential /foo/ubuntu-fde.dev-sda1.pin for /dev/sda1
+`,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestNotifyUserAuthResultFailureDifferentPath(c *C) {
+ s.testNotifyUserAuthResult(c, &testSdCredsNotifyUserAuthResultParams{
+ path: "/dev/nvme0n1p3",
+ credPath: "/foo/ubuntu-fde.dev-nvme0n1p3.passphrase",
+ result: UserAuthResultFailed,
+ authTypes: UserAuthTypePassphrase,
+ expectedMsg: `Incorrect passphrase from credential /foo/ubuntu-fde.dev-nvme0n1p3.passphrase for /dev/nvme0n1p3
+`,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestNotifyUserAuthResultInvalidPIN(c *C) {
+ s.testNotifyUserAuthResult(c, &testSdCredsNotifyUserAuthResultParams{
+ path: "/dev/sda1",
+ credPath: "/foo/ubuntu-fde.dev-sda1.pin",
+ result: UserAuthResultInvalidFormat,
+ authTypes: UserAuthTypePIN,
+ expectedMsg: `Incorrectly formatted PIN from credential /foo/ubuntu-fde.dev-sda1.pin
+`,
+ })
+}
+
+func (s *authRequestorSdCredsSuite) TestNotifyUserAuthResultInvalidRecoveryKey(c *C) {
+ s.testNotifyUserAuthResult(c, &testSdCredsNotifyUserAuthResultParams{
+ path: "/dev/sda1",
+ credPath: "/foo/ubuntu-fde.dev-sda1.recoverykey",
+ result: UserAuthResultInvalidFormat,
+ authTypes: UserAuthTypeRecoveryKey,
+ expectedMsg: `Incorrectly formatted recovery key from credential /foo/ubuntu-fde.dev-sda1.recoverykey
+`,
+ })
+}
diff --git a/auth_requestor_systemd_test.go b/auth_requestor_systemd_test.go
index b861d5aa..b7158bd9 100644
--- a/auth_requestor_systemd_test.go
+++ b/auth_requestor_systemd_test.go
@@ -60,6 +60,8 @@ type authRequestorSystemdSuite struct {
}
func (s *authRequestorSystemdSuite) SetUpTest(c *C) {
+ s.BaseTest.SetUpTest(c)
+
s.AddCleanup(s.authRequestorSystemdTestMixin.setUpTest(c))
}
diff --git a/export_test.go b/export_test.go
index 377ff677..9d8634ef 100644
--- a/export_test.go
+++ b/export_test.go
@@ -70,18 +70,20 @@ var (
)
type (
- ActivateConfigImpl = activateConfig
- ActivateConfigKey = activateConfigKey
- ActivateOneContainerStateMachine = activateOneContainerStateMachine
- ActivateOneContainerStateMachineFlags = activateOneContainerStateMachineFlags
- AutoAuthRequestor = autoAuthRequestor
- ExternalKeyData = externalKeyData
- ExternalUnlockKey = externalUnlockKey
- KdfParams = kdfParams
- PlymouthAuthRequestor = plymouthAuthRequestor
- PlymouthRequestUserCredentialContext = plymouthRequestUserCredentialContext
- ProtectedKeys = protectedKeys
- SystemdAuthRequestor = systemdAuthRequestor
+ ActivateConfigImpl = activateConfig
+ ActivateConfigKey = activateConfigKey
+ ActivateOneContainerStateMachine = activateOneContainerStateMachine
+ ActivateOneContainerStateMachineFlags = activateOneContainerStateMachineFlags
+ AutoAuthRequestor = autoAuthRequestor
+ ExternalKeyData = externalKeyData
+ ExternalUnlockKey = externalUnlockKey
+ KdfParams = kdfParams
+ PlymouthAuthRequestor = plymouthAuthRequestor
+ PlymouthRequestUserCredentialContext = plymouthRequestUserCredentialContext
+ ProtectedKeys = protectedKeys
+ SystemdAuthRequestor = systemdAuthRequestor
+ SystemdCredsAuthRequestor = systemdCredsAuthRequestor
+ SystemdCredsRequestUserCredentialContext = systemdCredsRequestUserCredentialContext
)
func KDFOptionsKdfParams(opts KDFOptions, defaultTargetDuration time.Duration, keyLen uint32) (*KdfParams, error) {
@@ -280,6 +282,14 @@ func MockNewSystemdAuthRequestor(fn func(io.Writer, SystemdAuthRequestorStringFn
}
}
+func MockNewSystemdCredsAuthRequestor(fn func(io.Writer, string) (AuthRequestor, error)) (restore func()) {
+ orig := newSystemdCredsAuthRequestor
+ newSystemdCredsAuthRequestor = fn
+ return func() {
+ newSystemdCredsAuthRequestor = orig
+ }
+}
+
func MockPBKDF2Benchmark(fn func(time.Duration, crypto.Hash) (uint, error)) (restore func()) {
orig := pbkdf2Benchmark
pbkdf2Benchmark = fn
@@ -370,6 +380,19 @@ func NewSystemdAuthRequestorForTesting(console io.Writer, stringFn SystemdAuthRe
}
}
+func (r *systemdCredsAuthRequestor) LastRequestUserCredentialCtx() systemdCredsRequestUserCredentialContext {
+ return r.lastRequestUserCredentialCtx
+}
+
+func NewSystemdCredsAuthRequestorForTesting(console io.Writer, prefix, credsDir string, lastRequestUserCredentialCtx *systemdCredsRequestUserCredentialContext) *systemdCredsAuthRequestor {
+ return &systemdCredsAuthRequestor{
+ console: console,
+ prefix: prefix,
+ credsDir: credsDir,
+ lastRequestUserCredentialCtx: *lastRequestUserCredentialCtx,
+ }
+}
+
func MockUnixStat(f func(devicePath string, st *unix.Stat_t) error) (restore func()) {
old := unixStat
unixStat = f