]> git.proxmox.com Git - proxmox-backup.git/blame - src/api2/config/tape_encryption_keys.rs
move acl to pbs_config workspaces, pbs_api_types cleanups
[proxmox-backup.git] / src / api2 / config / tape_encryption_keys.rs
CommitLineData
d5a48b5c
DM
1use anyhow::{bail, Error};
2use serde_json::Value;
3
4use proxmox::{
5 api::{
6 api,
7 ApiMethod,
8 Router,
9 RpcEnvironment,
ccdf327a 10 Permission,
d5a48b5c 11 },
d5a48b5c
DM
12};
13
5839c469
DM
14use pbs_api_types::{
15 Fingerprint, KeyInfo, Kdf,
16 TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
17 PROXMOX_CONFIG_DIGEST_SCHEMA, PASSWORD_HINT_SCHEMA,
8cc3760e 18 PRIV_TAPE_AUDIT, PRIV_TAPE_MODIFY,
5839c469
DM
19};
20
bbdda58b 21use pbs_config::key_config::KeyConfig;
21211748 22use pbs_config::open_backup_lockfile;
5839c469
DM
23use pbs_config::tape_encryption_keys::{
24 TAPE_KEYS_LOCKFILE,
25 load_keys,
26 load_key_configs,
27 save_keys,
28 save_key_configs,
29 insert_key,
30};
86fb3877 31
d5a48b5c 32#[api(
d5a48b5c
DM
33 input: {
34 properties: {},
35 },
36 returns: {
37 description: "The list of tape encryption keys (with config digest).",
38 type: Array,
69b8bc3b 39 items: { type: KeyInfo },
d5a48b5c 40 },
ccdf327a
DM
41 access: {
42 permission: &Permission::Privilege(&["tape", "pool"], PRIV_TAPE_AUDIT, false),
43 },
d5a48b5c
DM
44)]
45/// List existing keys
46pub fn list_keys(
47 _param: Value,
48 _info: &ApiMethod,
49 mut rpcenv: &mut dyn RpcEnvironment,
69b8bc3b 50) -> Result<Vec<KeyInfo>, Error> {
d5a48b5c 51
feb1645f 52 let (key_map, digest) = load_key_configs()?;
d5a48b5c
DM
53
54 let mut list = Vec::new();
feb1645f 55
69b8bc3b
DM
56 for (_fingerprint, item) in key_map.iter() {
57 list.push(item.into());
d5a48b5c 58 }
feb1645f 59
d5a48b5c
DM
60 rpcenv["digest"] = proxmox::tools::digest_to_hex(&digest).into();
61
62 Ok(list)
63}
301b8aa0
DM
64
65#[api(
4dafc513 66 protected: true,
301b8aa0
DM
67 input: {
68 properties: {
69 kdf: {
70 type: Kdf,
71 optional: true,
72 },
73 fingerprint: {
74 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
75 },
76 password: {
77 description: "The current password.",
78 min_length: 5,
79 },
80 "new-password": {
81 description: "The new password.",
82 min_length: 5,
83 },
84 hint: {
85 schema: PASSWORD_HINT_SCHEMA,
86 },
87 digest: {
88 optional: true,
89 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
90 },
91 },
92 },
ccdf327a
DM
93 access: {
94 permission: &Permission::Privilege(&["tape", "pool"], PRIV_TAPE_MODIFY, false),
95 },
301b8aa0
DM
96)]
97/// Change the encryption key's password (and password hint).
98pub fn change_passphrase(
99 kdf: Option<Kdf>,
100 password: String,
101 new_password: String,
102 hint: String,
103 fingerprint: Fingerprint,
104 digest: Option<String>,
105 _rpcenv: &mut dyn RpcEnvironment
106) -> Result<(), Error> {
107
108 let kdf = kdf.unwrap_or_default();
109
110 if let Kdf::None = kdf {
d1d74c43 111 bail!("Please specify a key derivation function (none is not allowed here).");
301b8aa0
DM
112 }
113
7526d864 114 let _lock = open_backup_lockfile(TAPE_KEYS_LOCKFILE, None, true)?;
301b8aa0
DM
115
116 let (mut config_map, expected_digest) = load_key_configs()?;
117
118 if let Some(ref digest) = digest {
119 let digest = proxmox::tools::hex_to_digest(digest)?;
120 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
121 }
122
123 let key_config = match config_map.get(&fingerprint) {
124 Some(key_config) => key_config,
125 None => bail!("tape encryption key '{}' does not exist.", fingerprint),
126 };
127
128 let (key, created, fingerprint) = key_config.decrypt(&|| Ok(password.as_bytes().to_vec()))?;
129 let mut new_key_config = KeyConfig::with_key(&key, new_password.as_bytes(), kdf)?;
130 new_key_config.created = created; // keep original value
301b8aa0
DM
131 new_key_config.hint = Some(hint);
132
133 config_map.insert(fingerprint, new_key_config);
134
135 save_key_configs(config_map)?;
136
137 Ok(())
138}
139
d5a48b5c
DM
140#[api(
141 protected: true,
142 input: {
143 properties: {
e5b6c933
DM
144 kdf: {
145 type: Kdf,
146 optional: true,
147 },
d5a48b5c
DM
148 password: {
149 description: "A secret password.",
150 min_length: 5,
151 },
152 hint: {
82a103c8 153 schema: PASSWORD_HINT_SCHEMA,
d5a48b5c
DM
154 },
155 },
156 },
feb1645f
DM
157 returns: {
158 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
159 },
ccdf327a
DM
160 access: {
161 permission: &Permission::Privilege(&["tape", "pool"], PRIV_TAPE_MODIFY, false),
162 },
d5a48b5c
DM
163)]
164/// Create a new encryption key
165pub fn create_key(
e5b6c933 166 kdf: Option<Kdf>,
d5a48b5c
DM
167 password: String,
168 hint: String,
169 _rpcenv: &mut dyn RpcEnvironment
170) -> Result<Fingerprint, Error> {
171
e5b6c933
DM
172 let kdf = kdf.unwrap_or_default();
173
174 if let Kdf::None = kdf {
d1d74c43 175 bail!("Please specify a key derivation function (none is not allowed here).");
e5b6c933
DM
176 }
177
1c86893d 178 let (key, mut key_config) = KeyConfig::new(password.as_bytes(), kdf)?;
82a103c8 179 key_config.hint = Some(hint);
d5a48b5c 180
feb1645f 181 let fingerprint = key_config.fingerprint.clone().unwrap();
d5a48b5c 182
18bd6ba1 183 insert_key(key, key_config, false)?;
d5a48b5c
DM
184
185 Ok(fingerprint)
186}
187
188
69b8bc3b
DM
189#[api(
190 input: {
191 properties: {
192 fingerprint: {
193 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
194 },
195 },
196 },
197 returns: {
198 type: KeyInfo,
199 },
ccdf327a
DM
200 access: {
201 permission: &Permission::Privilege(&["tape", "pool"], PRIV_TAPE_AUDIT, false),
202 },
69b8bc3b
DM
203)]
204/// Get key config (public key part)
205pub fn read_key(
206 fingerprint: Fingerprint,
207 _rpcenv: &mut dyn RpcEnvironment,
208) -> Result<KeyInfo, Error> {
209
210 let (config_map, _digest) = load_key_configs()?;
211
212 let key_config = match config_map.get(&fingerprint) {
213 Some(key_config) => key_config,
214 None => bail!("tape encryption key '{}' does not exist.", fingerprint),
215 };
216
217 if key_config.kdf.is_none() {
218 bail!("found unencrypted key - internal error");
219 }
220
221 Ok(key_config.into())
222}
223
d5a48b5c
DM
224#[api(
225 protected: true,
226 input: {
227 properties: {
228 fingerprint: {
229 schema: TAPE_ENCRYPTION_KEY_FINGERPRINT_SCHEMA,
230 },
231 digest: {
232 optional: true,
233 schema: PROXMOX_CONFIG_DIGEST_SCHEMA,
234 },
235 },
236 },
ccdf327a
DM
237 access: {
238 permission: &Permission::Privilege(&["tape", "pool"], PRIV_TAPE_MODIFY, false),
239 },
d5a48b5c
DM
240)]
241/// Remove a encryption key from the database
242///
243/// Please note that you can no longer access tapes using this key.
244pub fn delete_key(
245 fingerprint: Fingerprint,
246 digest: Option<String>,
247 _rpcenv: &mut dyn RpcEnvironment,
248) -> Result<(), Error> {
7526d864 249 let _lock = open_backup_lockfile(TAPE_KEYS_LOCKFILE, None, true)?;
d5a48b5c 250
feb1645f
DM
251 let (mut config_map, expected_digest) = load_key_configs()?;
252 let (mut key_map, _) = load_keys()?;
d5a48b5c
DM
253
254 if let Some(ref digest) = digest {
255 let digest = proxmox::tools::hex_to_digest(digest)?;
256 crate::tools::detect_modified_configuration_file(&digest, &expected_digest)?;
257 }
258
feb1645f
DM
259 match config_map.get(&fingerprint) {
260 Some(_) => { config_map.remove(&fingerprint); },
d5a48b5c
DM
261 None => bail!("tape encryption key '{}' does not exist.", fingerprint),
262 }
feb1645f 263 save_key_configs(config_map)?;
d5a48b5c 264
feb1645f 265 key_map.remove(&fingerprint);
d5a48b5c
DM
266 save_keys(key_map)?;
267
268 Ok(())
269}
270
271const ITEM_ROUTER: Router = Router::new()
69b8bc3b 272 .get(&API_METHOD_READ_KEY)
301b8aa0 273 .put(&API_METHOD_CHANGE_PASSPHRASE)
d5a48b5c
DM
274 .delete(&API_METHOD_DELETE_KEY);
275
276pub const ROUTER: Router = Router::new()
277 .get(&API_METHOD_LIST_KEYS)
278 .post(&API_METHOD_CREATE_KEY)
279 .match_all("fingerprint", &ITEM_ROUTER);