1

Say I want to change the behavior of kill for educational reasons. If a user directly types it in the shell, then nothing will happen. If some other program/entity-who-is-not-the-user calls it, it performs normally. A wrapping if-statement is probably sufficient, but what do I put in that if?

Edit I don't want to do this in the shell. I'm asking about kernel programming.

In line 2296 of the kernel source, kill is defined. I will wrap an if statement around the code inside. In that statement, there should be a check to see whether the one who called this was the user or just some process. The check is the part I don't know how to implement.

Regarding security

Goal:

  • Block the user from directly calling kill from any shell
  • Literally everything else is fine and will not be blocked
idlackage
  • 2,715
  • 8
  • 31
  • 52
  • Which system call? Which command? Edit your question to improve it – Basile Starynkevitch Nov 20 '15 at 05:25
  • Show the actual code and the real commands (which shell are you using?). Did you try coding a C program calling [kill(2)](http://man7.org/linux/man-pages/man2/kill.2.html)? How exactly did you modify the kernel? Did you try using `/bin/kill` instead of your shell `kill` builtin? So **edit your question** once more to improve it – Basile Starynkevitch Nov 20 '15 at 05:33
  • @Basile: I think the question is pretty clear, that he's trying to detect a `kill(2)` that happens in response to a keystroke or mouse click. Doing this in the `kill()` implementation in the kernel is super-weird. Doing it in the kernel, instead of in the app that handles the keystrokes or mouse clicks, is not possible to do accurately with no false positive or false negative errors. I think it's a silly question, because it's not something you can or should want to do, not because it's unclear. – Peter Cordes Nov 20 '15 at 06:16
  • But no `kill(2)` happens *directly* from a keystroke. All of them happens inside some process. And what about `echo kill 1234|at midnight` ? – Basile Starynkevitch Nov 20 '15 at 06:17
  • @BasileStarynkevitch: I just tweaked my previous comment. You're right, "direct" was the wrong word. I think the best answer to "I want to do this as a learning task" is what you've explained about why you shouldn't even try to do this. – Peter Cordes Nov 20 '15 at 06:20
  • 2
    I have the experience to answer your question. But, in order to limit my answer a bit, I'll need to ask a question or two as to your _intent_. Do you want to restrict a user [based on uid] from doing kill by any means? Or, just from the shell directly (e.g. if user at shell prompt, invokes "foobar", may the foobar program do a kill)? What about cron jobs for the user? Do you want to restrict the user from doing a shell `exec` command to a program not in /etc/shells (e.g. copies /bin/sh to ~user/sh and execs that). How "secure" do you want this? pls edit your post with more detail – Craig Estey Nov 20 '15 at 06:20
  • 1
    What I explained is that you should know what the ALP book is explaining before starting coding something inside the kernel; then you would ask a different, more focused and more precise, question and use different words. In particular *user* has very different meaning inside the kernel and in layman's terms. Hence I just voted to close the question as unclear – Basile Starynkevitch Nov 20 '15 at 06:32
  • You probably don't need to patch the kernel. Just patch the shell. – Basile Starynkevitch Nov 20 '15 at 06:41
  • 2
    @BasileStarynkevitch Having googled this for a long while I wanted to come here and not have to read through even more lengthy texts to finally arrive at an answer years later, but I see now, it makes sense why you kept saying it was unclear. When I have more time I'll definitely check out the book, thanks. – idlackage Nov 20 '15 at 06:43
  • The ALP book is freely available on line – Basile Starynkevitch Nov 20 '15 at 06:45
  • 1
    @idlackage Thanks for responding--it will help. As I mentioned to Basile, I will post a full answer, but it's 22:48 here. So, I'll have to push this until tomorrow morning as it will take a bit of thought to give you strategy, specifics, gotchas, caveats, etc. Maybe I'm the only one here that believes this is doable, but it is [I've been doing linux kernel for 20+]. It _will_ be a fair amount of work, probably more than you realize [I'll spell it out as best I can]. But, if you're truly interesting in trying, I'm truly interesting in helping where I can. – Craig Estey Nov 20 '15 at 06:56
  • 1
    @idlackage A followup question. What if the "foobar" program invokes a shell script that wants to kill? Since foobar is okay, I presume the subshell would be okay. What about foobar invoking a subshell with prompt? The more things you try to "plug", the harder it will become. So, think about it. List the security [as you've done], but prioritize the list, because detection of when/when not to inhibit can become tricky. I'll post yes/no and "degree of difficulty" based on your list. You may have to "pick your battles" based upon which can be won. – Craig Estey Nov 20 '15 at 07:02
  • 1
    @idlackage Well, think of it this way: You're my boss and you're writing a [small] requirements spec. I'll produce an answer that will be part functional spec, part design spec, some code, how to test, as well as a list of documents and some source files to look at. I'll also include some of my own techniques for debug, etc. I'll do my best to interpret what you want, but if you spell it out, what you get back will be closer to what you really wanted. I'll quantify as: if you want this feature, it will require hacking this file, etc. Take your time, I'm offline for the next 12 hours – Craig Estey Nov 20 '15 at 07:31
  • 1
    @idlackage P.S. Another reason to clarify your intent. I upvoted your question, but I see it's being downvoted. Basile recommended it be closed. Adding clarification may help stave that stuff off. Also, a truly complete answer to this is complex enough that it is possibly/probably worthy of a bounty [it is self serving of me to suggest it, I know]. Adding one might help you achieve what you want and stop some of the negativity. – Craig Estey Nov 20 '15 at 08:05
  • @CraigEstey Thanks for taking the time to help. I guess part of the problem here is that I don't know how to spell it out more. It's super clear in my mind and I don't understand how to make it even clearer than it already looks to me. I'll probably sleep on it (3am here) and see if I can make it better tomorrow and will put up the bounty when it's available. – idlackage Nov 20 '15 at 08:09
  • @Idlackage: what about the user writing some C source code and compiling it then running it? – Basile Starynkevitch Nov 20 '15 at 08:19
  • @idlackage Sleep well. I just realized two things: Tomorrow is Friday, so, IRL, I may have to push to post 6pm. Two: I believe I have enough points to edit your post to make it clearer. – Craig Estey Nov 20 '15 at 08:20
  • @CraigEstey: I am impatient to read your answer. AFAIU, you did have some time to sleep.... So I am waiting for your answer. What kernel patch do you suggest? (I suggested a simple one, but it could be very wrong) – Basile Starynkevitch Nov 21 '15 at 08:29
  • @BasileStarynkevitch Fri was washout because of IRL I had forgotten about. I've done the design, but as I was doing it, the subtleties, edge/corner cases starting showing up. There is/are more than one place a hook will be needed, as well as some specific new structs/state info. Also, strategy about how to go about actually doing it, etc. This is all written up on paper [the mere fact that I had to use _paper_ instead of editing the answer directly online indicates the difficulty], but, again, late here, but I _will_ enter it and post tomorrow (Sunday) – Craig Estey Nov 22 '15 at 07:04

3 Answers3

4

While other answers are technically true, I think they're being too strict regarding the question. What you want to do it not possible to do in a 100% reliable way, but you can get pretty close by making some reasonable assumptions.

Specifically if you define an interactive kill as:

  • called by process owned by a logged in user
  • called directly from/by a process named like a shell (it may be a new process, or it may be a built-in operation)
  • called by a process which is connected to a serial/pseudo-terminal (possibly also belonging to the logged in user)

then you can check for each of those properties when processing a syscall and make your choice that way.

There are ways this will not be reliable (sudo + expect + sh should work around most of these checks), but it may be enough to have fun with. How to implement those checks is a longer story and probably each point would deserve its own question. Check the documentation about users and pty devices - that should give you a good idea.

Edit: Actually, this may be even possible to implement as a LKM. Selinux can do similar kind of checks.

viraptor
  • 33,322
  • 10
  • 107
  • 191
2

It looks you are quite confused and do not understand what exactly a system call is and how does a Linux computer works. Everything is done inside some process thru system calls.

there should be a check to see whether the one who called this was directly done by the user or just some process

The above sentence has no sense. Everything is done by some process thru some system call. The notion of user exists only as an "attribute" of processes, see credentials(7) (so "directly done by the user" is vague). Read syscalls(2) and spend several days reading about Advanced Linux Programming, then ask a more focused question.

(I really believe you should not dare patching the kernel without knowing quite well what the ALP book above is explaining; then you would ask your question differently)

You should spend also several days or weeks reading about Operating Systems and Computer Architecture. You need to get a more precise idea of how a computer works, and that will take times (perhaps many years) and any answer here cannot cover all of it.

When the user types kill, he probably uses the shell builtin (type which kill and type kill) and the shell calls kill(2). When the user types /bin/kill he is execve(2) a program which will call kill(2). And the command might not come from the terminal (e.g. echo kill $$ | sh, the command is then coming from a pipe, or echo kill 1234|at midnight the kill is happening outside of user interaction and without any user interactively using the computer, the command being read from some file in /var/spool/cron/atjobs/, see atd(8)) In both cases the kernel only sees a SYS_kill system call.

BTW, modifying the kernel's behavior on kill could affect a lot of system software, so be careful when doing that. Read also signal(7) (some signals are not coming from a kill(2)).

You might use isatty(STDIN_FILENO) (see isatty(3)) to detect if a program is run in a terminal (no need to patch the kernel, you could just patch the shell). but I gave several cases where it is not. You -and your user- could also write a desktop application (using GTK or Qt) calling kill(2) and started on the desktop (it probably won't have any terminal attached when running, read about X11).

See also the notion of session and setsid(2); recent systemd based Linuxes have a notion of multi-seat which I am not familiar with (I don't know what kernel stuff is related to it).

If you only want to change the behavior of interactive terminals running some (well identified) shells, you need only to change the shell -with chsh(1)- (e.g. patch it to remove its kill builtin, and perhaps to avoid the shell doing an execve(2) of /bin/kill), no need to patch the kernel. But this won't prohibit the advanced user to code a small C program calling kill(2) (or even code his own shell in C and use it), compile his C source code, and run his freshly compiled ELF executable. See also restricted shell in bash.

If you just want to learn by making the exercise to patch the kernel and change its behavior for the kill(2) syscall, you need to define what process state you want to filter. So think in terms of processes making the kill(2) syscall, not in terms of "user" (processes do have several user ids)

BTW, patching the kernel is very difficult (if you want that to be reliable and safe), since by definition it is affecting your entire Linux system. The rule of thumb is to avoid patching the kernel when possible .... In your case, it looks like patching the shell could be enough for your goals, so prefer patching the shell (or perhaps patching the libc which is practically used by all shells...) to patching the kernel. See also LD_PRELOAD tricks.

Perhaps you just want the uid 1234 (assuming 1234 is the uid of your user) to be denied by your patched kernel using the kill(2) syscall (so he will need to have a setuid executable to do that), but your question is not formulated this way. That is probably simple to achieve, perhaps by adding in kill_ok_by_cred (near line 692 on Linux 4.4 file kernel/signal.c) something as simple as

 if (uid_eq(1234, tcred->uid))
    return 0;

But I might be completely wrong (I never patched the kernel, except for some drivers). Surely in a few hours Craig Ester would give a more authoritative answer.

Community
  • 1
  • 1
Basile Starynkevitch
  • 223,805
  • 18
  • 296
  • 547
  • I know that technically everything is 'some process'. I'm just wondering if there is a way to distinguish between that and direct user interaction with the shell. You can get the user id, current process id, etc. so it's possible that there could be a way, and that is what I want to find out. – idlackage Nov 20 '15 at 05:45
  • Please define what is *direct user interaction*. Before that, spend several days reading some of the references above, and a general Operating System textbook. I guess that you don't understand what *user* means. – Basile Starynkevitch Nov 20 '15 at 05:46
  • It's already been defined in the question...the user types `kill` in the shell. They are directly saying that they want to kill something. Thanks for repeatedly implying my stupidity but it's a simple question. I know where the code is, I know generally how to change it, I just don't know how to distinguish between what I'm defining as a direct call and an indirect call, if it's even possible. – idlackage Nov 20 '15 at 05:52
  • You really should read ALP, I cannot explain here all of it. My feeling is that your *direct call* vs *indirect call* distinguo has no sense. – Basile Starynkevitch Nov 20 '15 at 05:57
  • 1
    @BasileStarynkevitch Um, please ease up a bit. Can you think about all you want to say in your answer and then say it. I'm a kernel programmer with 40 years experience and I'm going to answer OP's question, but it's a bit distracting with your edits coming every 3 minutes. It is doable and with a bit of guidance he can have it up in about a week. Give the guy some credit for pinpointing the line from millions of lines of code in the kernel. – Craig Estey Nov 20 '15 at 06:01
  • @BasileStarynkevitch I posted a comment to OP in the main section asking for answers to clarifying questions. He responded with the info I need. But, it's 22:41 here, and I intend to post a full complete answer, so I probably will have to push this to tomorrow morning. I'll msg you directly after I post my answer. It will have methods, gotchas, and caveats regarding difficulty. OP's project is narrow enough (e.g. it's not like creating btrfs), that if he truly achieves it, he will learn a great deal about kernel devel, which is why I think he wants to do it. – Craig Estey Nov 20 '15 at 06:47
1

You can use aliases to change the behavior of commands. Aliases are only applied at interactive shells. Shell scripts ignore them. For example:

$ alias kill='echo hello'
$ kill
hello

If you want an alias to be available all the time, you could add it to ~/.bashrc (or whatever the equivalent file is if your shell isn't bash).

John Kugelman
  • 349,597
  • 67
  • 533
  • 578