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 }