From cb5d1fbc585528067c25cedc0011b4e78cc07ee9 Mon Sep 17 00:00:00 2001 From: Jeremy Soller Date: Wed, 5 Oct 2016 20:31:59 -0600 Subject: [PATCH] Add sudo command, add effective UID and GID, and groups file --- Makefile | 7 ++- filesystem/etc/group | 1 + kernel/context/context.rs | 18 ++++-- kernel/scheme/user.rs | 2 +- kernel/syscall/fs.rs | 10 +-- kernel/syscall/mod.rs | 2 + kernel/syscall/process.rs | 64 ++++++++++++------- libstd | 2 +- programs/id/src/main.rs | 2 - programs/login/src/main.rs | 7 ++- programs/sudo/Cargo.toml | 6 ++ programs/sudo/src/main.rs | 123 +++++++++++++++++++++++++++++++++++++ syscall/src/lib.rs | 8 +++ syscall/src/number.rs | 2 + 14 files changed, 214 insertions(+), 40 deletions(-) create mode 100644 filesystem/etc/group create mode 100644 programs/sudo/Cargo.toml create mode 100644 programs/sudo/src/main.rs diff --git a/Makefile b/Makefile index 43d0e21..6aa7c00 100644 --- a/Makefile +++ b/Makefile @@ -29,9 +29,12 @@ clean: cargo clean --manifest-path drivers/pcid/Cargo.toml cargo clean --manifest-path drivers/vesad/Cargo.toml cargo clean --manifest-path programs/getty/Cargo.toml + cargo clean --manifest-path programs/id/Cargo.toml cargo clean --manifest-path programs/init/Cargo.toml + cargo clean --manifest-path programs/sudo/Cargo.toml cargo clean --manifest-path programs/ion/Cargo.toml cargo clean --manifest-path programs/login/Cargo.toml + cargo clean --manifest-path programs/sudo/Cargo.toml cargo clean --manifest-path programs/coreutils/Cargo.toml cargo clean --manifest-path programs/extrautils/Cargo.toml cargo clean --manifest-path programs/smith/Cargo.toml @@ -279,7 +282,8 @@ $(BUILD)/filesystem.bin: \ filesystem/bin/id \ filesystem/bin/ion \ filesystem/bin/login \ - filesystem/bin/smith + filesystem/bin/smith \ + filesystem/bin/sudo rm -rf $@ $(BUILD)/filesystem/ echo exit | cargo run --manifest-path schemes/redoxfs/Cargo.toml --bin redoxfs-utility $@ 8 mkdir -p $(BUILD)/filesystem/ @@ -290,6 +294,7 @@ $(BUILD)/filesystem.bin: \ -chown -R 1000:1000 $(BUILD)/filesystem/home/user/ -chmod 700 $(BUILD)/filesystem/root/ -chmod 700 $(BUILD)/filesystem/home/user/ + -chmod +s $(BUILD)/filesystem/bin/sudo sync -fusermount -u $(BUILD)/filesystem/ rm -rf $(BUILD)/filesystem/ diff --git a/filesystem/etc/group b/filesystem/etc/group new file mode 100644 index 0000000..62fcd49 --- /dev/null +++ b/filesystem/etc/group @@ -0,0 +1 @@ +sudo;1;user diff --git a/kernel/context/context.rs b/kernel/context/context.rs index 512c66e..8c09573 100644 --- a/kernel/context/context.rs +++ b/kernel/context/context.rs @@ -22,10 +22,14 @@ pub struct Context { pub id: usize, /// The ID of the parent context pub ppid: usize, - /// The user id - pub uid: u32, - /// The group id - pub gid: u32, + /// The real user id + pub ruid: u32, + /// The real group id + pub rgid: u32, + /// The effective user id + pub euid: u32, + /// The effective group id + pub egid: u32, /// Status of context pub status: Status, /// Context running or not @@ -62,8 +66,10 @@ impl Context { Context { id: id, ppid: 0, - uid: 0, - gid: 0, + ruid: 0, + rgid: 0, + euid: 0, + egid: 0, status: Status::Blocked, running: false, vfork: false, diff --git a/kernel/scheme/user.rs b/kernel/scheme/user.rs index 542f01e..6bc25fd 100644 --- a/kernel/scheme/user.rs +++ b/kernel/scheme/user.rs @@ -38,7 +38,7 @@ impl UserInner { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - (context.id, context.uid, context.gid) + (context.id, context.euid, context.egid) }; let id = self.next_id.fetch_add(1, Ordering::SeqCst); diff --git a/kernel/syscall/fs.rs b/kernel/syscall/fs.rs index 1c969f6..f527c36 100644 --- a/kernel/syscall/fs.rs +++ b/kernel/syscall/fs.rs @@ -11,7 +11,7 @@ pub fn file_op(a: usize, fd: usize, c: usize, d: usize) -> Result { let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); let file = context.get_file(fd).ok_or(Error::new(EBADF))?; - (file, context.id, context.uid, context.gid) + (file, context.id, context.euid, context.egid) }; let scheme = { @@ -74,7 +74,7 @@ pub fn open(path: &[u8], flags: usize) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - (context.canonicalize(path), context.uid, context.gid) + (context.canonicalize(path), context.euid, context.egid) }; let mut parts = path_canon.splitn(2, |&b| b == b':'); @@ -107,7 +107,7 @@ pub fn mkdir(path: &[u8], mode: u16) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - (context.canonicalize(path), context.uid, context.gid) + (context.canonicalize(path), context.euid, context.egid) }; let mut parts = path_canon.splitn(2, |&b| b == b':'); @@ -129,7 +129,7 @@ pub fn rmdir(path: &[u8]) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - (context.canonicalize(path), context.uid, context.gid) + (context.canonicalize(path), context.euid, context.egid) }; let mut parts = path_canon.splitn(2, |&b| b == b':'); @@ -151,7 +151,7 @@ pub fn unlink(path: &[u8]) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - (context.canonicalize(path), context.uid, context.gid) + (context.canonicalize(path), context.euid, context.egid) }; let mut parts = path_canon.splitn(2, |&b| b == b':'); diff --git a/kernel/syscall/mod.rs b/kernel/syscall/mod.rs index c0d389e..fc357a6 100644 --- a/kernel/syscall/mod.rs +++ b/kernel/syscall/mod.rs @@ -55,6 +55,8 @@ pub extern fn syscall(a: usize, b: usize, c: usize, d: usize, e: usize, f: usize SYS_GETCWD => getcwd(validate_slice_mut(b as *mut u8, c)?), SYS_GETUID => getuid(), SYS_GETGID => getgid(), + SYS_GETEUID => geteuid(), + SYS_GETEGID => getegid(), SYS_SETUID => setuid(b as u32), SYS_SETGID => setgid(b as u32), SYS_PHYSALLOC => physalloc(b), diff --git a/kernel/syscall/process.rs b/kernel/syscall/process.rs index 9519ef2..e00d314 100644 --- a/kernel/syscall/process.rs +++ b/kernel/syscall/process.rs @@ -1,7 +1,7 @@ ///! Process syscalls use alloc::arc::Arc; use alloc::boxed::Box; -use collections::Vec; +use collections::{BTreeMap, Vec}; use core::mem; use core::str; use spin::Mutex; @@ -58,8 +58,10 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { let ppid; let pid; { - let uid; - let gid; + let ruid; + let rgid; + let euid; + let egid; let arch; let vfork; let mut kfx_option = None; @@ -80,8 +82,10 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { let context = context_lock.read(); ppid = context.id; - uid = context.uid; - gid = context.gid; + ruid = context.ruid; + rgid = context.rgid; + euid = context.euid; + egid = context.egid; arch = context.arch.clone(); @@ -191,7 +195,11 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { if flags & CLONE_VM == CLONE_VM { env = context.env.clone(); } else { - env = Arc::new(Mutex::new(context.env.lock().clone())); + let mut new_env = BTreeMap::new(); + for item in context.env.lock().iter() { + new_env.insert(item.0.clone(), Arc::new(Mutex::new(item.1.lock().clone()))); + } + env = Arc::new(Mutex::new(new_env)); } if flags & CLONE_FILES == CLONE_FILES { @@ -253,8 +261,10 @@ pub fn clone(flags: usize, stack_base: usize) -> Result { pid = context.id; context.ppid = ppid; - context.uid = uid; - context.gid = gid; + context.ruid = ruid; + context.rgid = rgid; + context.euid = euid; + context.egid = egid; context.status = context::Status::Runnable; @@ -436,7 +446,7 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - (context.uid, context.gid) + (context.euid, context.egid) }; let file = syscall::open(path, 0)?; @@ -483,11 +493,11 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { context.grants = Arc::new(Mutex::new(Vec::new())); if stat.st_mode & syscall::flag::MODE_SETUID == syscall::flag::MODE_SETUID { - context.uid = stat.st_uid; + context.euid = stat.st_uid; } if stat.st_mode & syscall::flag::MODE_SETGID == syscall::flag::MODE_SETGID { - context.gid = stat.st_gid; + context.egid = stat.st_gid; } // Map and copy new segments @@ -612,11 +622,25 @@ pub fn exec(path: &[u8], arg_ptrs: &[[usize; 2]]) -> Result { unsafe { usermode(entry, sp); } } +pub fn getegid() -> Result { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let context = context_lock.read(); + Ok(context.egid as usize) +} + +pub fn geteuid() -> Result { + let contexts = context::contexts(); + let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; + let context = context_lock.read(); + Ok(context.euid as usize) +} + pub fn getgid() -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - Ok(context.gid as usize) + Ok(context.rgid as usize) } pub fn getpid() -> Result { @@ -630,7 +654,7 @@ pub fn getuid() -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let context = context_lock.read(); - Ok(context.uid as usize) + Ok(context.ruid as usize) } pub fn iopl(_level: usize) -> Result { @@ -734,10 +758,9 @@ pub fn setgid(gid: u32) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let mut context = context_lock.write(); - if context.gid == 0 { - context.gid = gid; - Ok(0) - } else if context.gid == gid { + if context.egid == 0 { + context.rgid = gid; + context.egid = gid; Ok(0) } else { Err(Error::new(EPERM)) @@ -748,10 +771,9 @@ pub fn setuid(uid: u32) -> Result { let contexts = context::contexts(); let context_lock = contexts.current().ok_or(Error::new(ESRCH))?; let mut context = context_lock.write(); - if context.uid == 0 { - context.uid = uid; - Ok(0) - } else if context.uid == uid { + if context.euid == 0 { + context.ruid = uid; + context.euid = uid; Ok(0) } else { Err(Error::new(EPERM)) diff --git a/libstd b/libstd index 0484051..07d687d 160000 --- a/libstd +++ b/libstd @@ -1 +1 @@ -Subproject commit 0484051024316956c9e658ed7c3211f76a30a2f9 +Subproject commit 07d687dedcb9a8d76830fd54f6b992cbc3e49aa4 diff --git a/programs/id/src/main.rs b/programs/id/src/main.rs index 4e4d41b..b161353 100644 --- a/programs/id/src/main.rs +++ b/programs/id/src/main.rs @@ -1,5 +1,3 @@ -#![feature(question_mark)] - extern crate syscall; use std::env; diff --git a/programs/login/src/main.rs b/programs/login/src/main.rs index 6bb550f..079e870 100644 --- a/programs/login/src/main.rs +++ b/programs/login/src/main.rs @@ -6,9 +6,10 @@ extern crate termion; use octavo::octavo_digest::Digest; use octavo::octavo_digest::sha3::Sha512; use std::fs::File; -use std::io::{Read, Write}; -use std::process::{Command, CommandExt}; -use std::{io, str}; +use std::io::{self, Read, Write}; +use std::os::unix::process::CommandExt; +use std::process::Command; +use std::str; use termion::input::TermRead; pub struct Passwd<'a> { diff --git a/programs/sudo/Cargo.toml b/programs/sudo/Cargo.toml new file mode 100644 index 0000000..7ca45f7 --- /dev/null +++ b/programs/sudo/Cargo.toml @@ -0,0 +1,6 @@ +[package] +name = "sudo" +version = "0.1.0" + +[dependencies] +syscall = { path = "../../syscall/" } diff --git a/programs/sudo/src/main.rs b/programs/sudo/src/main.rs new file mode 100644 index 0000000..f9306f9 --- /dev/null +++ b/programs/sudo/src/main.rs @@ -0,0 +1,123 @@ +#![feature(question_mark)] + +extern crate syscall; + +use std::env; +use std::fs::File; +use std::io::Read; +use std::os::unix::process::CommandExt; +use std::process::{self, Command}; + +pub struct Passwd<'a> { + user: &'a str, + hash: &'a str, + uid: u32, + gid: u32, + name: &'a str, + home: &'a str, + shell: &'a str +} + +impl<'a> Passwd<'a> { + pub fn parse(line: &'a str) -> Result, ()> { + let mut parts = line.split(';'); + + let user = parts.next().ok_or(())?; + let hash = parts.next().ok_or(())?; + let uid = parts.next().ok_or(())?.parse::().or(Err(()))?; + let gid = parts.next().ok_or(())?.parse::().or(Err(()))?; + let name = parts.next().ok_or(())?; + let home = parts.next().ok_or(())?; + let shell = parts.next().ok_or(())?; + + Ok(Passwd { + user: user, + hash: hash, + uid: uid, + gid: gid, + name: name, + home: home, + shell: shell + }) + } +} + +pub struct Group<'a> { + group: &'a str, + gid: u32, + users: &'a str, +} + +impl<'a> Group<'a> { + pub fn parse(line: &'a str) -> Result, ()> { + let mut parts = line.split(';'); + + let group = parts.next().ok_or(())?; + let gid = parts.next().ok_or(())?.parse::().or(Err(()))?; + let users = parts.next().ok_or(())?; + + Ok(Group { + group: group, + gid: gid, + users: users + }) + } +} + +pub fn main() { + let mut args = env::args().skip(1); + let cmd = args.next().expect("sudo: no command provided"); + + let uid = syscall::getuid().unwrap() as u32; + + if uid != 0 { + let mut passwd_string = String::new(); + File::open("file:etc/passwd").unwrap().read_to_string(&mut passwd_string).unwrap(); + + let mut passwd_option = None; + for line in passwd_string.lines() { + if let Ok(passwd) = Passwd::parse(line) { + if uid == passwd.uid { + passwd_option = Some(passwd); + break; + } + } + } + + let passwd = passwd_option.expect("sudo: user not found in passwd"); + + let mut group_string = String::new(); + File::open("file:etc/group").unwrap().read_to_string(&mut group_string).unwrap(); + + let mut group_option = None; + for line in group_string.lines() { + if let Ok(group) = Group::parse(line) { + if group.group == "sudo" && group.users.split(',').any(|name| name == passwd.user) { + group_option = Some(group); + break; + } + } + } + + if group_option.is_none() { + panic!("sudo: '{}' not in sudo group", passwd.user); + } + } + + let mut command = Command::new(&cmd); + for arg in args { + command.arg(&arg); + } + + command.uid(0); + command.gid(0); + command.env("USER", "root"); + + match command.spawn() { + Ok(mut child) => match child.wait() { + Ok(status) => process::exit(status.code().unwrap_or(0)), + Err(err) => panic!("sudo: failed to wait for {}: {}", cmd, err) + }, + Err(err) => panic!("sudo: failed to execute {}: {}", cmd, err) + } +} diff --git a/syscall/src/lib.rs b/syscall/src/lib.rs index 94b7e65..8cc7916 100644 --- a/syscall/src/lib.rs +++ b/syscall/src/lib.rs @@ -88,6 +88,14 @@ pub fn getcwd(buf: &mut [u8]) -> Result { unsafe { syscall2(SYS_GETCWD, buf.as_mut_ptr() as usize, buf.len()) } } +pub fn getegid() -> Result { + unsafe { syscall0(SYS_GETEGID) } +} + +pub fn geteuid() -> Result { + unsafe { syscall0(SYS_GETEUID) } +} + pub fn getgid() -> Result { unsafe { syscall0(SYS_GETGID) } } diff --git a/syscall/src/number.rs b/syscall/src/number.rs index 525da1c..d5c5856 100644 --- a/syscall/src/number.rs +++ b/syscall/src/number.rs @@ -35,6 +35,8 @@ pub const SYS_EXECVE: usize = 11; pub const SYS_EXIT: usize = 1; pub const SYS_FUTEX: usize = 240; pub const SYS_GETCWD: usize = 183; +pub const SYS_GETEGID: usize = 202; +pub const SYS_GETEUID: usize = 201; pub const SYS_GETGID: usize = 200; pub const SYS_GETPID: usize = 20; pub const SYS_GETUID: usize = 199;