github.com/meulengracht/snapd@v0.0.0-20210719210640-8bde69bcc84e/overlord/hookstate/ctlcmd/fde_setup.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package ctlcmd
    21  
    22  import (
    23  	"encoding/json"
    24  	"fmt"
    25  
    26  	"github.com/snapcore/snapd/i18n"
    27  	"github.com/snapcore/snapd/kernel/fde"
    28  )
    29  
    30  type fdeSetupRequestCommand struct {
    31  	baseCommand
    32  }
    33  
    34  var shortFdeSetupRequestHelp = i18n.G("Obtain full disk encryption setup request")
    35  
    36  var longFdeSetupRequestHelp = i18n.G(`
    37  The fde-setup-request command is used inside the fde-setup hook. It will
    38  return information about what operation for full-disk encryption is
    39  requested and auxiliary data to complete this operation.
    40  
    41  The fde-setup hook should do what is requested and then call
    42  "snapctl fde-setup-result" and pass the result data to stdin.
    43  
    44  Here is an example for how the fde-setup hook is called initially:
    45  $ snapctl fde-setup-request
    46  {"op":"features"}
    47  $ echo  '{"features": []}' | snapctl fde-setup-result
    48  
    49  Alternatively the hook could reply with:
    50  $ echo '{"error":"hardware-unsupported"}' | snapctl fde-setup-result
    51  
    52  And then it is called again with a request to do the initial key setup:
    53  $ snapctl fde-setup-request
    54  {"op":"initial-setup", "key": "key-to-seal"}
    55  $ echo "{\"sealed-key\":\"$base64_encoded_sealed_key\"}" | snapctl fde-setup-result
    56  `)
    57  
    58  func init() {
    59  	addCommand("fde-setup-request", shortFdeSetupRequestHelp, longFdeSetupRequestHelp, func() command { return &fdeSetupRequestCommand{} })
    60  }
    61  
    62  func (c *fdeSetupRequestCommand) Execute(args []string) error {
    63  	context := c.context()
    64  	if context == nil {
    65  		return fmt.Errorf("cannot run fde-setup-request without a context")
    66  	}
    67  	context.Lock()
    68  	defer context.Unlock()
    69  
    70  	if context.HookName() != "fde-setup" {
    71  		return fmt.Errorf("cannot use fde-setup-request outside of the fde-setup hook")
    72  	}
    73  
    74  	var fdeSetup fde.SetupRequest
    75  	if err := context.Get("fde-setup-request", &fdeSetup); err != nil {
    76  		return fmt.Errorf("cannot get fde-setup-op from context: %v", err)
    77  	}
    78  	// Op is either "initial-setup" or "features"
    79  	switch fdeSetup.Op {
    80  	case "features", "initial-setup":
    81  		// fine
    82  	default:
    83  		return fmt.Errorf("unknown fde-setup-request op %q", fdeSetup.Op)
    84  
    85  	}
    86  
    87  	bytes, err := json.Marshal(fdeSetup)
    88  	if err != nil {
    89  		return fmt.Errorf("cannot json print fde key: %v", err)
    90  	}
    91  	c.printf("%s\n", string(bytes))
    92  
    93  	return nil
    94  }
    95  
    96  type fdeSetupResultCommand struct {
    97  	baseCommand
    98  }
    99  
   100  var shortFdeSetupResultHelp = i18n.G("Set result for full disk encryption")
   101  var longFdeSetupResultHelp = i18n.G(`
   102  The fde-setup-result command sets the result data for a fde-setup hook
   103  reading it from stdin.
   104  
   105  For example:
   106  When the fde-setup hook is called with "op":"features:
   107  $ echo '{"features": []}' | snapctl fde-setup-result
   108  
   109  When the fde-setup hook is called with "op":"initial-setup":
   110  $ echo "{\"sealed-key\":\"$base64_encoded_sealed_key\"}" | snapctl fde-setup-result
   111  `)
   112  
   113  func init() {
   114  	addCommand("fde-setup-result", shortFdeSetupResultHelp, longFdeSetupResultHelp, func() command { return &fdeSetupResultCommand{} })
   115  }
   116  
   117  func (c *fdeSetupResultCommand) Execute(args []string) error {
   118  	context := c.context()
   119  	if context == nil {
   120  		return fmt.Errorf("cannot run fde-setup-result without a context")
   121  	}
   122  	context.Lock()
   123  	defer context.Unlock()
   124  
   125  	if context.HookName() != "fde-setup" {
   126  		return fmt.Errorf("cannot use fde-setup-result outside of the fde-setup hook")
   127  	}
   128  
   129  	var fdeSetupResult []byte
   130  	if err := context.Get("stdin", &fdeSetupResult); err != nil {
   131  		return fmt.Errorf("internal error: cannot get result from stdin: %v", err)
   132  	}
   133  	if fdeSetupResult == nil {
   134  		return fmt.Errorf("no result data found from stdin")
   135  	}
   136  	context.Set("fde-setup-result", fdeSetupResult)
   137  
   138  	return nil
   139  }