Skip to content

Commit c3e4429

Browse files
committed
Reject sudo launchd service commands
1 parent 7b9bedc commit c3e4429

2 files changed

Lines changed: 27 additions & 0 deletions

File tree

garyx/src/service_manager/launchd.rs

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ impl LaunchdManager {
3737
if uid.is_empty() {
3838
return Err("empty uid from `id -u`".into());
3939
}
40+
validate_service_uid(&uid)?;
4041
Ok(uid)
4142
}
4243

@@ -215,6 +216,7 @@ impl ServiceManager for LaunchdManager {
215216
}
216217

217218
fn install(&self, spec: &ServiceSpec) -> Result<InstallReport, Box<dyn std::error::Error>> {
219+
self.uid()?;
218220
let plist_path = self.write_plist(spec)?;
219221
self.ensure_bootstrapped(&plist_path)?;
220222
self.kickstart(false)?;
@@ -226,6 +228,7 @@ impl ServiceManager for LaunchdManager {
226228
}
227229

228230
fn uninstall(&self) -> Result<(), Box<dyn std::error::Error>> {
231+
self.uid()?;
229232
// bootout first so launchd stops tracking the job, then rm the plist.
230233
self.bootout()?;
231234
let plist_path = self.plist_path()?;
@@ -236,6 +239,7 @@ impl ServiceManager for LaunchdManager {
236239
}
237240

238241
fn start(&self) -> Result<(), Box<dyn std::error::Error>> {
242+
self.uid()?;
239243
let plist_path = self.plist_path()?;
240244
if !plist_path.exists() {
241245
return Err(format!(
@@ -250,10 +254,12 @@ impl ServiceManager for LaunchdManager {
250254
}
251255

252256
fn stop(&self) -> Result<(), Box<dyn std::error::Error>> {
257+
self.uid()?;
253258
self.bootout()
254259
}
255260

256261
fn restart(&self, spec: &ServiceSpec) -> Result<InstallReport, Box<dyn std::error::Error>> {
262+
self.uid()?;
257263
let plist_path = self.write_plist(spec)?;
258264
self.ensure_bootstrapped(&plist_path)?;
259265
self.kickstart(true)?;
@@ -269,6 +275,16 @@ impl ServiceManager for LaunchdManager {
269275
}
270276
}
271277

278+
fn validate_service_uid(uid: &str) -> Result<(), Box<dyn std::error::Error>> {
279+
if uid == "0" {
280+
return Err(
281+
"`garyx gateway` service commands install a per-user macOS LaunchAgent; do not run them with sudo. Re-run as your normal login user, for example: `garyx gateway install`"
282+
.into(),
283+
);
284+
}
285+
Ok(())
286+
}
287+
272288
fn candidate_install_domains_for(
273289
uid: &str,
274290
is_aqua_session: bool,

garyx/src/service_manager/launchd/tests.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,16 @@
11
use super::*;
22

3+
#[test]
4+
fn validate_service_uid_rejects_root() {
5+
let err = validate_service_uid("0").expect_err("root should be rejected");
6+
assert!(err.to_string().contains("do not run them with sudo"));
7+
}
8+
9+
#[test]
10+
fn validate_service_uid_accepts_login_user() {
11+
validate_service_uid("501").expect("login user uid should be accepted");
12+
}
13+
314
#[test]
415
fn candidate_install_domains_prefers_gui_for_aqua_session() {
516
assert_eq!(

0 commit comments

Comments
 (0)