platform.rs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2019-2021 Tauri Programme within The Commons Conservancy
  2. // SPDX-License-Identifier: Apache-2.0
  3. // SPDX-License-Identifier: MIT
  4. //! Platform helper functions.
  5. use std::path::{PathBuf, MAIN_SEPARATOR};
  6. use crate::{Env, PackageInfo};
  7. mod starting_binary;
  8. /// Retrieves the currently running binary's path, taking into account security considerations.
  9. ///
  10. /// The path is cached as soon as possible (before even `main` runs) and that value is returned
  11. /// repeatedly instead of fetching the path every time. It is possible for the path to not be found,
  12. /// or explicitly disabled (see following macOS specific behavior).
  13. ///
  14. /// # Platform-specific behavior
  15. ///
  16. /// On `macOS`, this function will return an error if the original path contained any symlinks
  17. /// due to less protection on macOS regarding symlinks. This behavior can be disabled by setting the
  18. /// `process-relaunch-dangerous-allow-symlink-macos` feature, although it is *highly discouraged*.
  19. ///
  20. /// # Security
  21. ///
  22. /// If the above platform-specific behavior does **not** take place, this function uses the
  23. /// following resolution.
  24. ///
  25. /// We canonicalize the path we received from [`std::env::current_exe`] to resolve any soft links.
  26. /// This avoids the usual issue of needing the file to exist at the passed path because a valid
  27. /// current executable result for our purpose should always exist. Notably,
  28. /// [`std::env::current_exe`] also has a security section that goes over a theoretical attack using
  29. /// hard links. Let's cover some specific topics that relate to different ways an attacker might
  30. /// try to trick this function into returning the wrong binary path.
  31. ///
  32. /// ## Symlinks ("Soft Links")
  33. ///
  34. /// [`std::path::Path::canonicalize`] is used to resolve symbolic links to the original path,
  35. /// including nested symbolic links (`link2 -> link1 -> bin`). On macOS, any results that include
  36. /// a symlink are rejected by default due to lesser symlink protections. This can be disabled,
  37. /// **although discouraged**, with the `process-relaunch-dangerous-allow-symlink-macos` feature.
  38. ///
  39. /// ## Hard Links
  40. ///
  41. /// A [Hard Link] is a named entry that points to a file in the file system.
  42. /// On most systems, this is what you would think of as a "file". The term is
  43. /// used on filesystems that allow multiple entries to point to the same file.
  44. /// The linked [Hard Link] Wikipedia page provides a decent overview.
  45. ///
  46. /// In short, unless the attacker was able to create the link with elevated
  47. /// permissions, it should generally not be possible for them to hard link
  48. /// to a file they do not have permissions to - with exception to possible
  49. /// operating system exploits.
  50. ///
  51. /// There are also some platform-specific information about this below.
  52. ///
  53. /// ### Windows
  54. ///
  55. /// Windows requires a permission to be set for the user to create a symlink
  56. /// or a hard link, regardless of ownership status of the target. Elevated
  57. /// permissions users have the ability to create them.
  58. ///
  59. /// ### macOS
  60. ///
  61. /// macOS allows for the creation of symlinks and hard links to any file.
  62. /// Accessing through those links will fail if the user who owns the links
  63. /// does not have the proper permissions on the original file.
  64. ///
  65. /// ### Linux
  66. ///
  67. /// Linux allows for the creation of symlinks to any file. Accessing the
  68. /// symlink will fail if the user who owns the symlink does not have the
  69. /// proper permissions on the original file.
  70. ///
  71. /// Linux additionally provides a kernel hardening feature since version
  72. /// 3.6 (30 September 2012). Most distributions since then have enabled
  73. /// the protection (setting `fs.protected_hardlinks = 1`) by default, which
  74. /// means that a vast majority of desktop Linux users should have it enabled.
  75. /// **The feature prevents the creation of hardlinks that the user does not own
  76. /// or have read/write access to.** [See the patch that enabled this].
  77. ///
  78. /// [Hard Link]: https://en.wikipedia.org/wiki/Hard_link
  79. /// [See the patch that enabled this]: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/commit/?id=800179c9b8a1e796e441674776d11cd4c05d61d7
  80. pub fn current_exe() -> std::io::Result<PathBuf> {
  81. self::starting_binary::STARTING_BINARY.cloned()
  82. }
  83. /// Try to determine the current target triple.
  84. ///
  85. /// Returns a target triple (e.g. `x86_64-unknown-linux-gnu` or `i686-pc-windows-msvc`) or an
  86. /// `Error::Config` if the current config cannot be determined or is not some combination of the
  87. /// following values:
  88. /// `linux, mac, windows` -- `i686, x86, armv7` -- `gnu, musl, msvc`
  89. ///
  90. /// * Errors:
  91. /// * Unexpected system config
  92. pub fn target_triple() -> crate::Result<String> {
  93. let arch = if cfg!(target_arch = "x86") {
  94. "i686"
  95. } else if cfg!(target_arch = "x86_64") {
  96. "x86_64"
  97. } else if cfg!(target_arch = "arm") {
  98. "armv7"
  99. } else if cfg!(target_arch = "aarch64") {
  100. "aarch64"
  101. } else {
  102. return Err(crate::Error::Architecture);
  103. };
  104. let os = if cfg!(target_os = "linux") {
  105. "unknown-linux"
  106. } else if cfg!(target_os = "macos") {
  107. "apple-darwin"
  108. } else if cfg!(target_os = "windows") {
  109. "pc-windows"
  110. } else if cfg!(target_os = "freebsd") {
  111. "unknown-freebsd"
  112. } else {
  113. return Err(crate::Error::Os);
  114. };
  115. let os = if cfg!(target_os = "macos") || cfg!(target_os = "freebsd") {
  116. String::from(os)
  117. } else {
  118. let env = if cfg!(target_env = "gnu") {
  119. "gnu"
  120. } else if cfg!(target_env = "musl") {
  121. "musl"
  122. } else if cfg!(target_env = "msvc") {
  123. "msvc"
  124. } else {
  125. return Err(crate::Error::Environment);
  126. };
  127. format!("{}-{}", os, env)
  128. };
  129. Ok(format!("{}-{}", arch, os))
  130. }
  131. /// Computes the resource directory of the current environment.
  132. ///
  133. /// On Windows, it's the path to the executable.
  134. ///
  135. /// On Linux, when running in an AppImage the `APPDIR` variable will be set to
  136. /// the mounted location of the app, and the resource dir will be
  137. /// `${APPDIR}/usr/lib/${exe_name}`. If not running in an AppImage, the path is
  138. /// `/usr/lib/${exe_name}`. When running the app from
  139. /// `src-tauri/target/(debug|release)/`, the path is
  140. /// `${exe_dir}/../lib/${exe_name}`.
  141. ///
  142. /// On MacOS, it's `${exe_dir}../Resources` (inside .app).
  143. #[allow(unused_variables)]
  144. pub fn resource_dir(package_info: &PackageInfo, env: &Env) -> crate::Result<PathBuf> {
  145. let exe = current_exe()?;
  146. let exe_dir = exe.parent().expect("failed to get exe directory");
  147. let curr_dir = exe_dir.display().to_string();
  148. if curr_dir.ends_with(format!("{S}target{S}debug", S = MAIN_SEPARATOR).as_str())
  149. || curr_dir.ends_with(format!("{S}target{S}release", S = MAIN_SEPARATOR).as_str())
  150. || cfg!(target_os = "windows")
  151. {
  152. // running from the out dir or windows
  153. return Ok(exe_dir.to_path_buf());
  154. }
  155. #[allow(unused_mut, unused_assignments)]
  156. let mut res = Err(crate::Error::UnsupportedPlatform);
  157. #[cfg(target_os = "linux")]
  158. {
  159. res = if curr_dir.ends_with("/data/usr/bin") {
  160. // running from the deb bundle dir
  161. Ok(exe_dir.join(format!("../lib/{}", package_info.package_name())))
  162. } else if let Some(appdir) = &env.appdir {
  163. let appdir: &std::path::Path = appdir.as_ref();
  164. Ok(PathBuf::from(format!(
  165. "{}/usr/lib/{}",
  166. appdir.display(),
  167. package_info.package_name()
  168. )))
  169. } else {
  170. // running bundle
  171. Ok(PathBuf::from(format!(
  172. "/usr/lib/{}",
  173. package_info.package_name()
  174. )))
  175. };
  176. }
  177. #[cfg(target_os = "macos")]
  178. {
  179. res = Ok(exe_dir.join("../Resources"));
  180. }
  181. res
  182. }