main.rs 32 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018
  1. // SPDX-License-Identifier: AGPL-3.0-or-later
  2. use btconfig::{CredStoreConfig, FigmentExt, NodeCredConsumer};
  3. use btconsole::{BtConsole, BtConsoleConfig};
  4. use btfproto::{
  5. local_fs::{LocalFs, ModeAuthorizer},
  6. server::{new_fs_server, FsProvider},
  7. };
  8. use btlib::{bterr, crypto::Creds, error::DisplayErr, log::BuilderExt, Result};
  9. use btmsg::Receiver;
  10. use figment::{providers::Serialized, Figment};
  11. use serde::{Deserialize, Serialize};
  12. use std::{net::IpAddr, path::PathBuf, sync::Arc};
  13. #[derive(Debug, Serialize, Deserialize, Clone)]
  14. struct BtfsdConfig {
  15. #[serde(rename = "credstore")]
  16. cred_store: CredStoreConfig,
  17. /// The IP address to listen for filesystem connection on.
  18. #[serde(rename = "ipaddr")]
  19. ip_addr: IpAddr,
  20. /// The path in the local filesystem where blocks are stored.
  21. #[serde(rename = "blockdir")]
  22. block_dir: PathBuf,
  23. /// Configuration for the btconsole webapp.
  24. console: Option<BtConsoleConfig>,
  25. }
  26. impl BtfsdConfig {
  27. fn new() -> Result<BtfsdConfig> {
  28. Figment::new()
  29. .merge(Serialized::defaults(Self::default()))
  30. .btconfig()?
  31. .extract()
  32. .map_err(|err| err.into())
  33. }
  34. }
  35. impl Default for BtfsdConfig {
  36. fn default() -> Self {
  37. Self {
  38. cred_store: CredStoreConfig::default(),
  39. ip_addr: IpAddr::from([127, 0, 0, 1]),
  40. block_dir: btconfig::default_block_dir(),
  41. console: Some(BtConsoleConfig::default()),
  42. }
  43. }
  44. }
  45. async fn provider(block_dir: PathBuf, creds: Arc<dyn Creds>) -> Result<impl FsProvider> {
  46. if block_dir.exists() {
  47. LocalFs::new_existing(block_dir, creds, ModeAuthorizer)
  48. } else {
  49. std::fs::create_dir_all(&block_dir)?;
  50. LocalFs::new_empty(block_dir, 0, creds, ModeAuthorizer).await
  51. }
  52. }
  53. async fn receiver(config: BtfsdConfig) -> Result<Receiver> {
  54. let node_creds = config.cred_store.consume(NodeCredConsumer)??;
  55. let provider = Arc::new(provider(config.block_dir, node_creds.clone()).await?);
  56. new_fs_server(config.ip_addr, Arc::new(node_creds), provider)
  57. }
  58. #[tokio::main]
  59. async fn main() -> Result<()> {
  60. env_logger::Builder::from_default_env().btformat().init();
  61. let mut config = BtfsdConfig::new()?;
  62. let console_config = config
  63. .console
  64. .take()
  65. .ok_or_else(|| bterr!("No BtConsole configuration was provided"))?;
  66. let (console, receiver) = tokio::join!(BtConsole::start(console_config), receiver(config));
  67. let receiver = receiver?;
  68. let console = console?;
  69. log::debug!("ready to accept connections");
  70. let (console, receiver) = tokio::join!(console.has_shutdown(), receiver.complete()?);
  71. console?;
  72. receiver.display_err()?;
  73. Ok(())
  74. }
  75. #[cfg(test)]
  76. mod tests {
  77. use super::*;
  78. use btconfig::CredStoreConfig;
  79. use btconsole::BtConsoleConfig;
  80. use btfproto::{client::FsClient, msg::*};
  81. use btlib::{
  82. crypto::{
  83. file_cred_store::FileCredStore, tpm::TpmCredStore, ConcreteCreds, CredStore,
  84. CredStoreMut, CredsPriv, CredsPub,
  85. },
  86. log::BuilderExt,
  87. AuthzAttrs, BlockMetaSecrets, Epoch, IssuedProcRec, Principaled, ProcRec,
  88. };
  89. use btlib_tests::{CredStoreTestingExt, TpmCredStoreHarness};
  90. use btmsg::BlockAddr;
  91. use btserde::from_slice;
  92. use std::net::{IpAddr, Ipv4Addr};
  93. use std::{future::ready, net::Ipv6Addr, time::Duration};
  94. use swtpm_harness::SwtpmHarness;
  95. use tempdir::TempDir;
  96. const LOG_LEVEL: &str = "warn";
  97. #[ctor::ctor]
  98. fn ctor() {
  99. std::env::set_var("RUST_LOG", LOG_LEVEL);
  100. env_logger::Builder::from_default_env().btformat().init();
  101. }
  102. struct FileTestCase {
  103. client: FsClient,
  104. _rx: Receiver,
  105. _dir: TempDir,
  106. }
  107. async fn file_test_case(dir: TempDir, ipaddr: IpAddr) -> FileTestCase {
  108. let file_store_path = dir.path().join("cred_store");
  109. let credstore = CredStoreConfig::File {
  110. path: file_store_path.clone(),
  111. };
  112. {
  113. let cred_store = FileCredStore::new(file_store_path).unwrap();
  114. cred_store.provision(ROOT_PASSWD).unwrap();
  115. }
  116. let config = BtfsdConfig {
  117. cred_store: credstore,
  118. ip_addr: ipaddr,
  119. block_dir: dir.path().join(BT_DIR),
  120. console: Some(BtConsoleConfig::default()),
  121. };
  122. let rx = receiver(config).await.unwrap();
  123. let tx = rx.transmitter(rx.addr().clone()).await.unwrap();
  124. let client = FsClient::new(tx);
  125. FileTestCase {
  126. _dir: dir,
  127. _rx: rx,
  128. client,
  129. }
  130. }
  131. async fn new_file_test_case() -> FileTestCase {
  132. let dir = TempDir::new("btfsd").unwrap();
  133. file_test_case(dir, LOCALHOST).await
  134. }
  135. struct TestCase {
  136. client: FsClient,
  137. rx: Receiver,
  138. harness: TpmCredStoreHarness,
  139. _dir: TempDir,
  140. }
  141. const ROOT_PASSWD: &str = "existential_threat";
  142. const LOCALHOST: IpAddr = IpAddr::V6(Ipv6Addr::LOCALHOST);
  143. const BT_DIR: &str = "bt";
  144. async fn tpm_test_case(dir: TempDir, harness: TpmCredStoreHarness, ipaddr: IpAddr) -> TestCase {
  145. let swtpm = harness.swtpm();
  146. let credstore = CredStoreConfig::Tpm {
  147. path: swtpm.state_path().to_owned(),
  148. tabrmd: swtpm.tabrmd_config().to_owned(),
  149. };
  150. let config = BtfsdConfig {
  151. cred_store: credstore,
  152. ip_addr: ipaddr,
  153. block_dir: dir.path().join(BT_DIR),
  154. console: Some(BtConsoleConfig::default()),
  155. };
  156. let rx = receiver(config).await.unwrap();
  157. let tx = rx.transmitter(rx.addr().clone()).await.unwrap();
  158. let client = FsClient::new(tx);
  159. TestCase {
  160. _dir: dir,
  161. harness,
  162. rx,
  163. client,
  164. }
  165. }
  166. async fn new_tpm_test_case() -> TestCase {
  167. let dir = TempDir::new("btfsd").unwrap();
  168. let harness = TpmCredStoreHarness::new(ROOT_PASSWD.to_owned()).unwrap();
  169. tpm_test_case(dir, harness, LOCALHOST).await
  170. }
  171. async fn existing_case(case: TestCase) -> TestCase {
  172. case.rx.stop().unwrap();
  173. case.rx.complete().unwrap().await.unwrap();
  174. let TestCase {
  175. _dir,
  176. harness: _harness,
  177. ..
  178. } = case;
  179. tpm_test_case(_dir, _harness, IpAddr::V4(Ipv4Addr::LOCALHOST)).await
  180. }
  181. #[allow(dead_code)]
  182. async fn manual_test() {
  183. let case = new_tpm_test_case().await;
  184. case.rx.complete().unwrap().await.unwrap();
  185. }
  186. async fn create_write_read(client: FsClient) {
  187. const FILENAME: &str = "file.txt";
  188. const EXPECTED: &[u8] = b"potato";
  189. let CreateReply { inode, handle, .. } = client
  190. .create(
  191. SpecInodes::RootDir.into(),
  192. FILENAME,
  193. FlagValue::ReadWrite.into(),
  194. 0o644,
  195. 0,
  196. )
  197. .await
  198. .unwrap();
  199. let WriteReply { written, .. } = client.write(inode, handle, 0, EXPECTED).await.unwrap();
  200. assert_eq!(EXPECTED.len() as u64, written);
  201. let actual = client
  202. .read(inode, handle, 0, EXPECTED.len() as u64, |reply| {
  203. let mut buf = Vec::with_capacity(EXPECTED.len());
  204. buf.extend_from_slice(reply.data);
  205. ready(buf)
  206. })
  207. .await
  208. .unwrap();
  209. assert_eq!(EXPECTED, &actual);
  210. }
  211. #[tokio::test]
  212. async fn create_write_read_with_tpm() {
  213. let case = new_tpm_test_case().await;
  214. create_write_read(case.client).await;
  215. }
  216. #[tokio::test]
  217. async fn create_write_read_with_file() {
  218. let case = new_file_test_case().await;
  219. create_write_read(case.client).await;
  220. }
  221. #[tokio::test]
  222. async fn read_full_sector() {
  223. const FILENAME: &str = "file.txt";
  224. const EXPECTED: &[u8] = b"prawn crisps";
  225. let case = new_tpm_test_case().await;
  226. let client = case.client;
  227. let CreateReply {
  228. inode,
  229. handle,
  230. entry,
  231. ..
  232. } = client
  233. .create(
  234. SpecInodes::RootDir.into(),
  235. FILENAME,
  236. FlagValue::ReadWrite.into(),
  237. 0o644,
  238. 0,
  239. )
  240. .await
  241. .unwrap();
  242. let WriteReply { written, .. } = client.write(inode, handle, 0, EXPECTED).await.unwrap();
  243. assert_eq!(EXPECTED.len() as u64, written);
  244. assert!(entry.attr.sect_sz > EXPECTED.len() as u64);
  245. let actual = client
  246. .read(inode, handle, 0, entry.attr.sect_sz, |reply| {
  247. let mut buf = Vec::with_capacity(EXPECTED.len());
  248. buf.extend_from_slice(reply.data);
  249. ready(buf)
  250. })
  251. .await
  252. .unwrap();
  253. assert!(EXPECTED.eq(&actual));
  254. }
  255. #[tokio::test]
  256. async fn read_from_different_instance() {
  257. const FILENAME: &str = "file.txt";
  258. const EXPECTED: &[u8] = b"potato";
  259. let case = new_tpm_test_case().await;
  260. let client = &case.client;
  261. let CreateReply { inode, handle, .. } = client
  262. .create(
  263. SpecInodes::RootDir.into(),
  264. FILENAME,
  265. FlagValue::ReadWrite.into(),
  266. 0o644,
  267. 0,
  268. )
  269. .await
  270. .unwrap();
  271. let WriteReply { written, .. } = client.write(inode, handle, 0, EXPECTED).await.unwrap();
  272. assert_eq!(EXPECTED.len() as u64, written);
  273. client.flush(inode, handle).await.unwrap();
  274. let case = existing_case(case).await;
  275. let client = &case.client;
  276. let LookupReply { inode, .. } = client
  277. .lookup(SpecInodes::RootDir.into(), FILENAME)
  278. .await
  279. .unwrap();
  280. let OpenReply { handle, .. } = client
  281. .open(inode, FlagValue::ReadOnly.into())
  282. .await
  283. .unwrap();
  284. let actual = client
  285. .read(inode, handle, 0, EXPECTED.len() as u64, |reply| {
  286. let mut buf = Vec::with_capacity(EXPECTED.len());
  287. buf.extend_from_slice(reply.data);
  288. ready(buf)
  289. })
  290. .await
  291. .unwrap();
  292. assert_eq!(EXPECTED, &actual);
  293. }
  294. #[tokio::test]
  295. async fn create_lookup() {
  296. const FILENAME: &str = "file.txt";
  297. let case = new_tpm_test_case().await;
  298. let client = case.client;
  299. let CreateReply {
  300. inode: expected, ..
  301. } = client
  302. .create(
  303. SpecInodes::RootDir.into(),
  304. FILENAME,
  305. FlagValue::ReadOnly.into(),
  306. 0o644,
  307. 0,
  308. )
  309. .await
  310. .unwrap();
  311. let LookupReply { inode: actual, .. } = client
  312. .lookup(SpecInodes::RootDir.into(), FILENAME)
  313. .await
  314. .unwrap();
  315. assert_eq!(expected, actual);
  316. }
  317. #[tokio::test]
  318. async fn open_existing() {
  319. const FILENAME: &str = "file.txt";
  320. let case = new_tpm_test_case().await;
  321. let client = case.client;
  322. let CreateReply { inode, .. } = client
  323. .create(
  324. SpecInodes::RootDir.into(),
  325. FILENAME,
  326. FlagValue::ReadOnly.into(),
  327. 0o644,
  328. 0,
  329. )
  330. .await
  331. .unwrap();
  332. let result = client.open(inode, FlagValue::ReadWrite.into()).await;
  333. assert!(result.is_ok());
  334. }
  335. #[tokio::test]
  336. async fn write_flush_close_read() {
  337. const FILENAME: &str = "lyrics.txt";
  338. const EXPECTED: &[u8] = b"Fate, or something better";
  339. let case = new_tpm_test_case().await;
  340. let client = &case.client;
  341. let CreateReply { inode, handle, .. } = client
  342. .create(
  343. SpecInodes::RootDir.into(),
  344. FILENAME,
  345. FlagValue::ReadWrite.into(),
  346. 0o644,
  347. 0,
  348. )
  349. .await
  350. .unwrap();
  351. let WriteReply { written, .. } = client.write(inode, handle, 0, EXPECTED).await.unwrap();
  352. assert_eq!(EXPECTED.len() as u64, written);
  353. client.flush(inode, handle).await.unwrap();
  354. client.close(inode, handle).await.unwrap();
  355. let OpenReply { handle, .. } = client
  356. .open(inode, FlagValue::ReadOnly.into())
  357. .await
  358. .unwrap();
  359. let actual = client
  360. .read(inode, handle, 0, EXPECTED.len() as u64, |reply| {
  361. ready(reply.data.to_owned())
  362. })
  363. .await
  364. .unwrap();
  365. assert_eq!(EXPECTED, &actual);
  366. }
  367. #[tokio::test]
  368. async fn link() {
  369. const FIRSTNAME: &str = "Jean-Luc";
  370. const LASTNAME: &str = "Picard";
  371. let case = new_tpm_test_case().await;
  372. let client = &case.client;
  373. let CreateReply { inode, .. } = client
  374. .create(
  375. SpecInodes::RootDir.into(),
  376. FIRSTNAME,
  377. Flags::default(),
  378. 0o644,
  379. 0,
  380. )
  381. .await
  382. .unwrap();
  383. client
  384. .link(inode, SpecInodes::RootDir.into(), LASTNAME)
  385. .await
  386. .unwrap();
  387. let OpenReply {
  388. handle: root_handle,
  389. ..
  390. } = client
  391. .open(
  392. SpecInodes::RootDir.into(),
  393. FlagValue::ReadOnly | FlagValue::Directory,
  394. )
  395. .await
  396. .unwrap();
  397. let ReadDirReply { entries, .. } = client
  398. .read_dir(SpecInodes::RootDir.into(), root_handle, 0, 0)
  399. .await
  400. .unwrap();
  401. let filenames: Vec<_> = entries.iter().map(|e| e.0.as_str()).collect();
  402. assert!(filenames.contains(&FIRSTNAME));
  403. assert!(filenames.contains(&LASTNAME));
  404. }
  405. #[tokio::test]
  406. async fn unlink() {
  407. const FIRSTNAME: &str = "Jean-Luc";
  408. const LASTNAME: &str = "Picard";
  409. let case = new_tpm_test_case().await;
  410. let client = &case.client;
  411. let CreateReply { inode, .. } = client
  412. .create(
  413. SpecInodes::RootDir.into(),
  414. FIRSTNAME,
  415. Flags::default(),
  416. 0o644,
  417. 0,
  418. )
  419. .await
  420. .unwrap();
  421. client
  422. .link(inode, SpecInodes::RootDir.into(), LASTNAME)
  423. .await
  424. .unwrap();
  425. client
  426. .unlink(SpecInodes::RootDir.into(), FIRSTNAME)
  427. .await
  428. .unwrap();
  429. let OpenReply {
  430. handle: root_handle,
  431. ..
  432. } = client
  433. .open(
  434. SpecInodes::RootDir.into(),
  435. FlagValue::ReadOnly | FlagValue::Directory,
  436. )
  437. .await
  438. .unwrap();
  439. let ReadDirReply { entries, .. } = client
  440. .read_dir(SpecInodes::RootDir.into(), root_handle, 0, 0)
  441. .await
  442. .unwrap();
  443. let filenames: Vec<_> = entries.iter().map(|e| e.0.as_str()).collect();
  444. assert!(filenames.contains(&LASTNAME));
  445. assert!(!filenames.contains(&FIRSTNAME));
  446. }
  447. #[tokio::test]
  448. async fn delete() {
  449. const FILENAME: &str = "MANIFESTO.tex";
  450. let case = new_tpm_test_case().await;
  451. let client = &case.client;
  452. client
  453. .create(
  454. SpecInodes::RootDir.into(),
  455. FILENAME,
  456. Flags::default(),
  457. 0o644,
  458. 0,
  459. )
  460. .await
  461. .unwrap();
  462. client
  463. .unlink(SpecInodes::RootDir.into(), FILENAME)
  464. .await
  465. .unwrap();
  466. let OpenReply {
  467. handle: root_handle,
  468. ..
  469. } = client
  470. .open(
  471. SpecInodes::RootDir.into(),
  472. FlagValue::ReadOnly | FlagValue::Directory,
  473. )
  474. .await
  475. .unwrap();
  476. let ReadDirReply { entries, .. } = client
  477. .read_dir(SpecInodes::RootDir.into(), root_handle, 0, 0)
  478. .await
  479. .unwrap();
  480. let filenames: Vec<_> = entries.iter().map(|e| e.0.as_str()).collect();
  481. assert!(!filenames.contains(&FILENAME));
  482. }
  483. #[tokio::test]
  484. async fn read_meta() {
  485. const FILENAME: &str = "kibosh.txt";
  486. const EXPECTED: u32 = 0o600;
  487. let case = new_tpm_test_case().await;
  488. let client = &case.client;
  489. let before_create = Epoch::now();
  490. let CreateReply { inode, handle, .. } = client
  491. .create(
  492. SpecInodes::RootDir.into(),
  493. FILENAME,
  494. FlagValue::ReadWrite.into(),
  495. EXPECTED,
  496. 0,
  497. )
  498. .await
  499. .unwrap();
  500. let before_read = Epoch::now();
  501. let ReadMetaReply { attrs, .. } = client.read_meta(inode, Some(handle)).await.unwrap();
  502. let actual = attrs.mode;
  503. assert_eq!(FileType::Reg | EXPECTED, actual);
  504. assert!(before_create <= attrs.ctime && attrs.ctime <= before_read);
  505. assert!(before_create <= attrs.mtime && attrs.mtime <= before_read);
  506. assert!(before_create <= attrs.atime && attrs.atime <= before_read);
  507. assert_eq!(attrs.block_id.inode, inode);
  508. assert_eq!(attrs.size, 0);
  509. assert!(attrs.tags.is_empty());
  510. }
  511. #[tokio::test]
  512. async fn write_meta() {
  513. fn assert_eq(expected: &Attrs, actual: &BlockMetaSecrets) {
  514. assert_eq!(expected.mode, actual.mode);
  515. assert_eq!(expected.uid, actual.uid);
  516. assert_eq!(expected.gid, actual.gid);
  517. assert_eq!(expected.atime, actual.atime);
  518. assert_eq!(expected.mtime, actual.mtime);
  519. assert_eq!(expected.ctime, actual.ctime);
  520. assert_eq!(expected.tags.len(), actual.tags.len());
  521. for (key, expected_value) in expected.tags.iter() {
  522. let actual_value = actual.tags.get(key).unwrap();
  523. assert_eq!(expected_value, actual_value);
  524. }
  525. }
  526. const FILENAME: &str = "word_salad.docx";
  527. let expected = Attrs {
  528. mode: 0o600,
  529. uid: 9290,
  530. gid: 2190,
  531. atime: 23193.into(),
  532. mtime: 53432.into(),
  533. ctime: 87239.into(),
  534. tags: Vec::new(),
  535. };
  536. let case = new_tpm_test_case().await;
  537. let client = &case.client;
  538. let CreateReply { inode, handle, .. } = client
  539. .create(
  540. SpecInodes::RootDir.into(),
  541. FILENAME,
  542. FlagValue::ReadWrite.into(),
  543. expected.mode | 0o011,
  544. 0,
  545. )
  546. .await
  547. .unwrap();
  548. let WriteMetaReply { attrs, .. } = client
  549. .write_meta(inode, Some(handle), expected.clone(), AttrsSet::ALL)
  550. .await
  551. .unwrap();
  552. assert_eq(&expected, &attrs);
  553. let ReadMetaReply { attrs, .. } = client.read_meta(inode, Some(handle)).await.unwrap();
  554. assert_eq(&expected, &attrs);
  555. }
  556. #[tokio::test]
  557. async fn allocate_when_empty() {
  558. const FILENAME: &str = "output.dat";
  559. const EXPECTED: &[u8] = &[0u8; 8];
  560. let case = new_tpm_test_case().await;
  561. let client = &case.client;
  562. let CreateReply { inode, handle, .. } = client
  563. .create(
  564. SpecInodes::RootDir.into(),
  565. FILENAME,
  566. FlagValue::ReadWrite.into(),
  567. 0o644,
  568. 0,
  569. )
  570. .await
  571. .unwrap();
  572. client
  573. .allocate(inode, handle, EXPECTED.len() as u64)
  574. .await
  575. .unwrap();
  576. let actual = client
  577. .read(inode, handle, 0, EXPECTED.len() as u64, |reply| {
  578. ready(Vec::from(reply.data))
  579. })
  580. .await
  581. .unwrap();
  582. assert_eq!(EXPECTED, actual);
  583. }
  584. #[tokio::test]
  585. async fn allocate_with_data_present() {
  586. const FILENAME: &str = "output.dat";
  587. const EXPECTED: &[u8] = &[1, 1, 1, 1, 0, 0, 0, 0];
  588. let case = new_tpm_test_case().await;
  589. let client = &case.client;
  590. let CreateReply { inode, handle, .. } = client
  591. .create(
  592. SpecInodes::RootDir.into(),
  593. FILENAME,
  594. FlagValue::ReadWrite.into(),
  595. 0o644,
  596. 0,
  597. )
  598. .await
  599. .unwrap();
  600. client.write(inode, handle, 0, &[1, 1, 1, 1]).await.unwrap();
  601. client
  602. .allocate(inode, handle, EXPECTED.len() as u64)
  603. .await
  604. .unwrap();
  605. let actual = client
  606. .read(inode, handle, 0, EXPECTED.len() as u64, |reply| {
  607. ready(Vec::from(reply.data))
  608. })
  609. .await
  610. .unwrap();
  611. assert_eq!(EXPECTED, actual);
  612. }
  613. #[tokio::test]
  614. async fn forget() {
  615. const FILENAME: &str = "seed.dat";
  616. let case = new_tpm_test_case().await;
  617. let client = &case.client;
  618. let CreateReply { inode, handle, .. } = client
  619. .create(
  620. SpecInodes::RootDir.into(),
  621. FILENAME,
  622. FlagValue::ReadWrite.into(),
  623. 0o644,
  624. 0,
  625. )
  626. .await
  627. .unwrap();
  628. client.close(inode, handle).await.unwrap();
  629. let result = client.forget(inode, 1).await;
  630. assert!(result.is_ok());
  631. }
  632. #[tokio::test]
  633. async fn add_readcap() {
  634. const FILENAME: &str = "net";
  635. let case = new_tpm_test_case().await;
  636. let client = &case.client;
  637. let creds = ConcreteCreds::generate().unwrap();
  638. let CreateReply { inode, handle, .. } = client
  639. .create(
  640. SpecInodes::RootDir.into(),
  641. FILENAME,
  642. FlagValue::ReadWrite | FlagValue::Directory,
  643. 0o755,
  644. 0,
  645. )
  646. .await
  647. .unwrap();
  648. client
  649. .add_readcap(inode, handle, creds.concrete_pub())
  650. .await
  651. .unwrap();
  652. }
  653. #[tokio::test]
  654. async fn grant_access_to_root() {
  655. let case = new_tpm_test_case().await;
  656. let client = &case.client;
  657. let mut creds = ConcreteCreds::generate().unwrap();
  658. let root_creds = case.harness.root_creds().unwrap();
  659. let writecap = root_creds
  660. .issue_writecap(
  661. creds.principal(),
  662. &mut std::iter::empty(),
  663. Epoch::now() + Duration::from_secs(3600),
  664. )
  665. .unwrap();
  666. creds.set_writecap(writecap).unwrap();
  667. let expected = IssuedProcRec {
  668. addr: IpAddr::V4(Ipv4Addr::LOCALHOST),
  669. pub_creds: creds.concrete_pub(),
  670. writecap: creds.writecap().unwrap().to_owned(),
  671. authz_attrs: AuthzAttrs {
  672. uid: 9001,
  673. gid: 9001,
  674. supp_gids: vec![12, 41, 19],
  675. },
  676. };
  677. client
  678. .grant_access(SpecInodes::RootDir.into(), expected.clone())
  679. .await
  680. .unwrap();
  681. let LookupReply { inode, entry, .. } = client
  682. .lookup(SpecInodes::RootDir.into(), &creds.principal().to_string())
  683. .await
  684. .unwrap();
  685. let OpenReply { handle, .. } = client
  686. .open(inode, FlagValue::ReadOnly.into())
  687. .await
  688. .unwrap();
  689. let record = client
  690. .read(inode, handle, 0, entry.attr.size, |reply| {
  691. ready(from_slice::<ProcRec>(reply.data))
  692. })
  693. .await
  694. .unwrap()
  695. .unwrap();
  696. let actual = record.validate().unwrap();
  697. assert_eq!(expected, actual);
  698. }
  699. #[tokio::test]
  700. async fn grant_access_to_non_root_dir() {
  701. const DIRNAME: &str = "var";
  702. let case = new_tpm_test_case().await;
  703. let client = &case.client;
  704. let mut creds = ConcreteCreds::generate().unwrap();
  705. let root_creds = case.harness.root_creds().unwrap();
  706. let writecap = root_creds
  707. .issue_writecap(
  708. creds.principal(),
  709. &mut [DIRNAME].into_iter(),
  710. Epoch::now() + Duration::from_secs(3600),
  711. )
  712. .unwrap();
  713. creds.set_writecap(writecap).unwrap();
  714. let expected = IssuedProcRec {
  715. addr: IpAddr::V4(Ipv4Addr::LOCALHOST),
  716. pub_creds: creds.concrete_pub(),
  717. writecap: creds.writecap().unwrap().to_owned(),
  718. authz_attrs: AuthzAttrs {
  719. uid: 9001,
  720. gid: 9001,
  721. supp_gids: vec![12, 41, 19],
  722. },
  723. };
  724. let CreateReply { inode, handle, .. } = client
  725. .create(
  726. SpecInodes::RootDir.into(),
  727. DIRNAME,
  728. FlagValue::Directory | FlagValue::ReadWrite,
  729. 0o755,
  730. 0,
  731. )
  732. .await
  733. .unwrap();
  734. client.close(inode, handle).await.unwrap();
  735. client.grant_access(inode, expected.clone()).await.unwrap();
  736. let LookupReply {
  737. inode: record_inode,
  738. entry,
  739. ..
  740. } = client
  741. .lookup(inode, &creds.principal().to_string())
  742. .await
  743. .unwrap();
  744. let OpenReply {
  745. handle: record_handle,
  746. ..
  747. } = client
  748. .open(record_inode, FlagValue::ReadOnly.into())
  749. .await
  750. .unwrap();
  751. let record = client
  752. .read(record_inode, record_handle, 0, entry.attr.size, |reply| {
  753. ready(from_slice::<ProcRec>(reply.data))
  754. })
  755. .await
  756. .unwrap()
  757. .unwrap();
  758. let actual = record.validate().unwrap();
  759. assert_eq!(expected, actual);
  760. }
  761. #[tokio::test]
  762. async fn grant_access_non_root_user() {
  763. const ROOT_FILE: &str = "root.txt";
  764. const USER_FILE: &str = "user.txt";
  765. let user_tpm = SwtpmHarness::new().unwrap();
  766. let case = new_tpm_test_case().await;
  767. let client = &case.client;
  768. let root_creds = case.harness.root_creds().unwrap();
  769. let user_creds = {
  770. let cred_store = TpmCredStore::from_context(
  771. user_tpm.context().unwrap(),
  772. user_tpm.state_path().to_owned(),
  773. )
  774. .unwrap();
  775. let mut creds = cred_store.node_creds().unwrap();
  776. let writecap = root_creds
  777. .issue_writecap(
  778. creds.principal(),
  779. &mut std::iter::empty(),
  780. Epoch::now() + Duration::from_secs(3600),
  781. )
  782. .unwrap();
  783. cred_store
  784. .assign_node_writecap(&mut creds, writecap)
  785. .unwrap();
  786. creds
  787. };
  788. let expected = IssuedProcRec {
  789. addr: IpAddr::V4(Ipv4Addr::LOCALHOST),
  790. pub_creds: user_creds.concrete_pub(),
  791. writecap: user_creds.writecap().unwrap().to_owned(),
  792. authz_attrs: AuthzAttrs {
  793. uid: 9001,
  794. gid: 9001,
  795. supp_gids: vec![12, 41, 19],
  796. },
  797. };
  798. let root_inode = SpecInodes::RootDir.into();
  799. client
  800. .grant_access(root_inode, expected.clone())
  801. .await
  802. .unwrap();
  803. // Note that non-root users do not have permission to write to ROOT_FILE, but
  804. // UID 9001 has permission to write to USER_FILE.
  805. client
  806. .create(root_inode, ROOT_FILE, FlagValue::ReadWrite.into(), 0o644, 0)
  807. .await
  808. .unwrap();
  809. let CreateReply { inode, handle, .. } = client
  810. .create(root_inode, USER_FILE, FlagValue::ReadWrite.into(), 0o644, 0)
  811. .await
  812. .unwrap();
  813. let mut attrs = Attrs::default();
  814. attrs.uid = expected.authz_attrs.uid;
  815. attrs.gid = expected.authz_attrs.gid;
  816. client
  817. .write_meta(inode, Some(handle), attrs, AttrsSet::UID | AttrsSet::GID)
  818. .await
  819. .unwrap();
  820. let node_creds = case.harness.cred_store().node_creds().unwrap();
  821. let bind_path = node_creds.writecap().unwrap().bind_path();
  822. let block_addr = Arc::new(BlockAddr::new(LOCALHOST, Arc::new(bind_path)));
  823. let tx = btmsg::Transmitter::new(block_addr, Arc::new(user_creds))
  824. .await
  825. .unwrap();
  826. let client = FsClient::new(tx);
  827. let root_dir = SpecInodes::RootDir.value();
  828. let LookupReply { inode, .. } = client.lookup(root_dir, USER_FILE).await.unwrap();
  829. let result = client.open(inode, FlagValue::ReadWrite.into()).await;
  830. assert!(result.is_ok());
  831. let LookupReply { inode, .. } = client.lookup(root_dir, ROOT_FILE).await.unwrap();
  832. let result = client.open(inode, FlagValue::ReadWrite.into()).await;
  833. let err = result.err().unwrap().to_string();
  834. assert_eq!("write access denied", err);
  835. }
  836. }
  837. #[cfg(test)]
  838. mod config_tests {
  839. use super::BtfsdConfig;
  840. use std::{
  841. net::{IpAddr, SocketAddr},
  842. path::PathBuf,
  843. };
  844. use btconfig::CredStoreConfig;
  845. use btconsole::BtConsoleConfig;
  846. use figment::Jail;
  847. #[test]
  848. fn cred_store_file_path() {
  849. Jail::expect_with(|jail| {
  850. let expected = PathBuf::from("./file_credstore");
  851. jail.set_env("BT_CREDSTORE_TYPE", "File");
  852. jail.set_env("BT_CREDSTORE_PATH", expected.display());
  853. let config = BtfsdConfig::new().unwrap();
  854. let success = if let CredStoreConfig::File { path: actual } = config.cred_store {
  855. expected == actual
  856. } else {
  857. false
  858. };
  859. assert!(success);
  860. Ok(())
  861. })
  862. }
  863. #[test]
  864. fn cred_store_tpm_path_tabrmd() {
  865. Jail::expect_with(|jail| {
  866. let expected_path = PathBuf::from("./tpm_credstore");
  867. let expected_tabrmd = String::from("bus_type=session");
  868. jail.set_env("BT_CREDSTORE_TYPE", "Tpm");
  869. jail.set_env("BT_CREDSTORE_PATH", expected_path.display());
  870. jail.set_env("BT_CREDSTORE_TABRMD", expected_tabrmd.as_str());
  871. let config = BtfsdConfig::new().unwrap();
  872. let success = if let CredStoreConfig::Tpm {
  873. path: actual_path,
  874. tabrmd: actual_tabrmd,
  875. } = config.cred_store
  876. {
  877. expected_path == actual_path && expected_tabrmd == actual_tabrmd
  878. } else {
  879. false
  880. };
  881. assert!(success);
  882. Ok(())
  883. })
  884. }
  885. #[test]
  886. fn ip_addr() {
  887. Jail::expect_with(|jail| {
  888. let expected = IpAddr::from([172, 0, 0, 1]);
  889. jail.set_env("BT_IPADDR", expected);
  890. let config = BtfsdConfig::new().unwrap();
  891. assert_eq!(expected, config.ip_addr);
  892. Ok(())
  893. })
  894. }
  895. #[test]
  896. fn block_dir() {
  897. Jail::expect_with(|jail| {
  898. let expected = PathBuf::from("/tmp/blocks");
  899. jail.set_env("BT_BLOCKDIR", expected.display());
  900. let config = BtfsdConfig::new().unwrap();
  901. assert_eq!(expected, config.block_dir);
  902. Ok(())
  903. })
  904. }
  905. #[test]
  906. fn console() {
  907. Jail::expect_with(|jail| {
  908. let expected_addr = SocketAddr::from(([127, 0, 0, 42], 4129));
  909. let expected_dist_path = PathBuf::from("/var/www");
  910. jail.set_env("BT_CONSOLE_ADDR", expected_addr);
  911. jail.set_env("BT_CONSOLE_DISTPATH", expected_dist_path.display());
  912. let expected = BtConsoleConfig::new(expected_addr, expected_dist_path);
  913. let config = BtfsdConfig::new().unwrap();
  914. assert_eq!(expected, config.console.unwrap());
  915. Ok(())
  916. })
  917. }
  918. }