Skip to content

Commit

Permalink
daemon: add /v2/system-secureboot endpoint for pushing updates to sec…
Browse files Browse the repository at this point in the history
…ureboot key DBs

Add a new endpoint for integration with a local manager of EFI
secureboot key databases.

Signed-off-by: Maciej Borzecki <[email protected]>
  • Loading branch information
bboozzoo committed Oct 3, 2024
1 parent 5333fe2 commit 1bb4610
Show file tree
Hide file tree
Showing 4 changed files with 470 additions and 0 deletions.
1 change: 1 addition & 0 deletions daemon/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ var api = []*Command{
registryCmd,
noticesCmd,
noticeCmd,
systemSecurebootCmd,
}

const (
Expand Down
166 changes: 166 additions & 0 deletions daemon/api_system_secureboot.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
// -*- Mode: Go; indent-tabs-mode: t -*-

/*
* Copyright (C) 2024 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 <http://www.gnu.org/licenses/>.
*
*/

package daemon

import (
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"net/http"

"github.com/snapcore/snapd/overlord/auth"
"github.com/snapcore/snapd/overlord/fdestate"
)

var systemSecurebootCmd = &Command{
// TODO GET returning whether secure boot is relevant for the system?

Path: "/v2/system-secureboot",
POST: postSystemSecurebootAction,
WriteAccess: interfaceProviderRootAccess{
// TODO find a specialized interface for this, but for now assume that
// requests will come only from snaps plugging fwupd interface on the
// slot side, which also allows manipulation of EFI variables
Interfaces: []string{"fwupd"},
},
}

func postSystemSecurebootAction(c *Command, r *http.Request, user *auth.UserState) Response {
contentType := r.Header.Get("Content-Type")

switch contentType {
case "application/json":
return postSystemSecurebootActionJSON(c, r)
default:
return BadRequest("unexpected content type: %q", contentType)
}
}

type securebootRequest struct {
Action string `json:"action,omitempty"`

// Payload is a base64 encoded binary blob, is used in
// efi-secureboot-db-prepare action, and carries the DBX update content. The
// blob is in the range from few kB to tens of kBs
Payload string `json:"payload,omitempty"`

// DB is used with efi-secureboot-db-prepare action, and indicates the
// secureboot keys DB which is a target of the action, possible values are
// PK, KEK, DB, DBX
DB string `json:"db,omitempty"`
}

func (r *securebootRequest) Validate() error {
switch r.Action {
case "efi-secureboot-update-startup", "efi-secureboot-update-db-cleanup":
if r.DB != "" {
return fmt.Errorf("unexpected key DB for action %q", r.Action)
}

if len(r.Payload) > 0 {
return fmt.Errorf("unexpected payload for action %q", r.Action)
}
case "efi-secureboot-update-db-prepare":
switch r.DB {
case "PK", "KEK", "DB", "DBX":
default:
return fmt.Errorf("invalid key DB %q", r.DB)
}

if len(r.Payload) == 0 {
return errors.New("update payload not provided")
}
default:
return fmt.Errorf("unsupported EFI secure boot action %q", r.Action)
}
return nil
}

func postSystemSecurebootActionJSON(c *Command, r *http.Request) Response {
var req securebootRequest

decoder := json.NewDecoder(r.Body)

if err := decoder.Decode(&req); err != nil {
return BadRequest("cannot decode request body: %v", err)
}

if decoder.More() {
return BadRequest("extra content found in request body")
}

if err := req.Validate(); err != nil {
return BadRequest(err.Error())
}

switch req.Action {
case "efi-secureboot-update-startup":
return postSystemActionEFISecurebootUpdateStartup(c)
case "efi-secureboot-update-db-cleanup":
return postSystemActionEFISecurebootUpdateDBCleanup(c)
case "efi-secureboot-update-db-prepare":
return postSystemActionEFISecurebootUpdateDBPrepare(c, &req)
default:
return InternalError("support for EFI secure boot action %q is not implemented", req.Action)
}
}

var fdestateEFISecureBootDBUpdatePrepare = fdestate.EFISecureBootDBUpdatePrepare

func postSystemActionEFISecurebootUpdateDBPrepare(c *Command, req *securebootRequest) Response {
if req.DB != "DBX" {
return InternalError("support for key DB %q is not implemented", req.DB)
}

payload, err := base64.StdEncoding.DecodeString(req.Payload)
if err != nil {
return BadRequest("cannot decode payload: %v", err)
}

err = fdestateEFISecureBootDBUpdatePrepare(c.d.state,
fdestate.EFISecurebootDBX, // only DBX updates are supported
payload)
if err != nil {
return BadRequest("cannot notify of update prepare: %v", err)
}

return SyncResponse(nil)
}

var fdestateEFISecureBootDBUpdateCleanup = fdestate.EFISecureBootDBUpdateCleanup

func postSystemActionEFISecurebootUpdateDBCleanup(c *Command) Response {
if err := fdestateEFISecureBootDBUpdateCleanup(c.d.state); err != nil {
return BadRequest("cannot notify of update cleanup: %v", err)
}

return SyncResponse(nil)
}

var fdestateEFISecureBootDBManagerStartup = fdestate.EFISecureBootDBManagerStartup

func postSystemActionEFISecurebootUpdateStartup(c *Command) Response {
if err := fdestateEFISecureBootDBManagerStartup(c.d.state); err != nil {
return BadRequest("cannot notify of manager startup: %v", err)
}

return SyncResponse(nil)
}
Loading

0 comments on commit 1bb4610

Please sign in to comment.