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