As always, thx in advance,
--
------------------------------------------------------------------------------
Joshua Bardwell | "May be going to hell in a bucket, baby
Internet: gt6...@prism.gatech.edu | But at least I'm enjoying the ride!"
------------------------------------------------------------------------------
What driver are you using?
Gudu
>>I'm curious -- could some of you more experienced mudlib coders post or
>>email some of the axioms of mudlib security? I'm working on a lib now,
>>and if I just say "Oh well I'll add the security later" Im' going to leave
>>holes all over the place so I'd like to get this done right the first time
>>and maybe learn from those who went before me.
>What driver are you using?
Oops -- Silly me :)
MudOS 0.9.20
Axioms? The only Axiom of mud security is "if you dont trust a wizard,
then they shouldnt BE a wizard"... Note this doesnt have anything to
do about this wizard having actually done anything you can prove... If
you dont trust them, yah dont give em the keys to the car...
-James
A full discussion of security would take up more bandwidth than I am
willing to put forth here, but here are a couple simple pointers if
you plan to use the Mudos uid system (which I think is a plenty secure
system... flame if you like).
Keep the number of root objects to a minimum. No need for an object
to have privs it doesn't need. This is analogous to the "need to
know" paradigm. Have several uids for various functions of the mud,
e.g. Socket, Mail, Snoop, Finger.
Don't trust this_player(), or this_player(1). Both are easily
subvertable by taking advantage of the init() apply. An attacker
simply moves an object into the inventory of a admin user. In the
init() of that object is placed something like:
write_file("/players/evil.o", "level 10000"). During the execution of
the code in init(), this_player() will return the admin player object
thus allowing the attack to work. Do not be niave in thinking that
using this_player(1) will stop this attack. It is just as simple to
move the attacker object in a call_out(), thus this_player(1) still
returns the admin's player object again.
Instead of the above, use a previous_object() check. Here is a basic
security check:
if( geteuid( previous_object()) != UID_NEEDED_TO_PREFORM_TASK)
/* No go */
To further tighten this security an addition check can be added in
locations where you see fit:
if( geteuid( this_player(1)) != UID_NEEDED_TO_PREFORM_TASK)
This check disallows objects with admin uids from having any speical
privs when not used by an admin. BUT be forwarned, that the above
check is worthless without the former check.
If you plan to use a command bin structure my suggestion is allow these
objects to set their euid to the euid of their previous object. This
is analogous to how non-suid unix commands operate. This type of system
is much safer than giving root to all the command objects.
Don't ever try to temporarily hand a high privs uid to an object via
export_uid() so that the object can prefrom a privaledged task
(e.g. saving itself). This is frought with danger. An attacker can,
depending on the case, cause the code to break while the uid is root.
Be wary of any call_others() is secure code. Make sure that the functions
that are being called are as secure as the code that is calling them.
Be aware of the effects of destruct(). After an object is destructed
its code continues to operate, but many things will not work. For
example, no output via write_file(), write(), printf(), etc... will
occur. So if you have a secure function which does some logging for
security, make sure the object has not been dested before it does the
logging. (Thanks chuck :))
These are just a few pointers from of fthe top of my head. Ellery
discusses these and several others in a short document that can be
found (last I knew) at TMI-2.
>I'm curious -- could some of you more experienced mudlib coders post or
>email some of the axioms of mudlib security? I'm working on a lib now,
>and if I just say "Oh well I'll add the security later" Im' going to leave
>holes all over the place so I'd like to get this done right the first time
>and maybe learn from those who went before me.
I have put a lot of thought into security over time. Bertrand
Reimers suggestion of using the entire call chain as the seucrity
method is far and away the most prefereable. It suffers from the least security
holes that I can think of.
Basicly it is an extension of a fairly normal euid security ssytem.
Every object has privilages on where it can write given to it
by its tag. You trace back up the call tree and if anyone
in the call_tree cannot write to a given place, then the write will
not succeed.
You use a special construct of the form unguarded, to cut the tree
so that specific writes or reads can occur to partiular areas. Like
you alwyas want a plyer file to be able to save, even though the calling
object may not have the priliages itself.
This can easily be done by specifiing an object as a guard object and
stopping the tag checking upon reachine this object.
Hope this makes at least vague sense,
David.
[DDT] Pink fish forever.
I am not flaming, but so far there has been no mudlib based upon the
above system (which is fairly driver independent) that I didn't manage
to break into in less than half an hour (that's with ed access; with
having access to a tared version, cut that down to 10 minutes).
(I should like to point out that in some cases people thought they
were clever by not giving me read access to the master object -
unfortunately, that didn't help them a lot.)
: Keep the number of root objects to a minimum. No need for an object
: to have privs it doesn't need. This is analogous to the "need to
: know" paradigm. Have several uids for various functions of the mud,
: e.g. Socket, Mail, Snoop, Finger.
Actually, uids of admins work just as well if you want to break into
a system.
: Don't trust this_player(), or this_player(1). Both are easily
: subvertable by taking advantage of the init() apply. An attacker
: simply moves an object into the inventory of a admin user. In the
: init() of that object is placed something like:
To forge this_player(1), you need add_action(). init() won't suffice
here.
: write_file("/players/evil.o", "level 10000"). During the execution of
: the code in init(), this_player() will return the admin player object
: thus allowing the attack to work. Do not be niave in thinking that
: using this_player(1) will stop this attack. It is just as simple to
: move the attacker object in a call_out(), thus this_player(1) still
: returns the admin's player object again.
In a call_out(), this_player(1) normally is 0. However, add_action()
does the trick.
: Instead of the above, use a previous_object() check. Here is a basic
: security check:
: if( geteuid( previous_object()) != UID_NEEDED_TO_PREFORM_TASK)
: /* No go */
Right. The drawback is that your mudli will be littered with such checks.
And humans aren't perfect. One check or another will be wrong. Like
checking geteuid(this_player()). Or forgetting to check. Or forgetting
to make a function static.
: To further tighten this security an addition check can be added in
: locations where you see fit:
: if( geteuid( this_player(1)) != UID_NEEDED_TO_PREFORM_TASK)
"To further tighten this security" sounds weird. If the former isn't
secure, it is easy to forge the latter. Security should be a a boolean
property at best.
: This check disallows objects with admin uids from having any speical
: privs when not used by an admin. BUT be forwarned, that the above
: check is worthless without the former check.
And that the latter check is either necessary or superfluous.
: If you plan to use a command bin structure my suggestion is allow these
: objects to set their euid to the euid of their previous object. This
: is analogous to how non-suid unix commands operate. This type of system
: is much safer than giving root to all the command objects.
Why? Any command that assumes the euid of previous_object() will sooner
or later take on that of an admin. Write an object to check for that
regularly, and chances are you'll be in, if the object has got a hole.
: Don't ever try to temporarily hand a high privs uid to an object via
: export_uid() so that the object can prefrom a privaledged task
: (e.g. saving itself). This is frought with danger. An attacker can,
: depending on the case, cause the code to break while the uid is root.
And this of course applies to everything else that temporarily changes
euids/uids.
: Be wary of any call_others() is secure code. Make sure that the functions
: that are being called are as secure as the code that is calling them.
My biggest complaint about the security system. A call_other(), one of
the most basic LPC instructions, is inherently unsafe.
[...]
Reimer Behrends
Much as I feel honoured being apparently semi-mistaken for Bertrand
Meyer, this is not how my name is spelled. ;-)
Reimer Behrends
Well, you are indeed an exceptional individual, for we have had many a
person try to hack into our mud and all have failed. No security
system is fail safe. You can only hope to keep the majority of people
out and keep good backups.
>: Don't trust this_player(), or this_player(1). Both are easily
>: subvertable by taking advantage of the init() apply. An attacker
>: simply moves an object into the inventory of a admin user. In the
>: init() of that object is placed something like:
>
>To forge this_player(1), you need add_action(). init() won't suffice
>here.
>
>: write_file("/players/evil.o", "level 10000"). During the execution of
>: the code in init(), this_player() will return the admin player object
>: thus allowing the attack to work. Do not be niave in thinking that
>: using this_player(1) will stop this attack. It is just as simple to
>: move the attacker object in a call_out(), thus this_player(1) still
>: returns the admin's player object again.
>
>In a call_out(), this_player(1) normally is 0. However, add_action()
>does the trick.
Not true, I just tested this on a 0.9.20 driver. this_player(1) ==
this_player() in a call_out. Maybe this is not true with 3.2.1?
>: Instead of the above, use a previous_object() check. Here is a basic
>: security check:
>
>: if( geteuid( previous_object()) != UID_NEEDED_TO_PREFORM_TASK)
>: /* No go */
>
>Right. The drawback is that your mudli will be littered with such checks.
>And humans aren't perfect. One check or another will be wrong. Like
>checking geteuid(this_player()). Or forgetting to check. Or forgetting
>to make a function static.
Well, use a macro or sefun then. Your alternative is make no access
the default I assume. I would be interested in hearing some discussion
on the feasibilty, flexibilty and performance of such a system.
>: To further tighten this security an addition check can be added in
>: locations where you see fit:
>
>: if( geteuid( this_player(1)) != UID_NEEDED_TO_PREFORM_TASK)
>
>"To further tighten this security" sounds weird. If the former isn't
>secure, it is easy to forge the latter. Security should be a a boolean
>property at best.
What I should have said is "To further restrict access". In most cases you
don't want the second restriction.
>: If you plan to use a command bin structure my suggestion is allow these
>: objects to set their euid to the euid of their previous object. This
>: is analogous to how non-suid unix commands operate. This type of system
>: is much safer than giving root to all the command objects.
>
>Why? Any command that assumes the euid of previous_object() will sooner
>or later take on that of an admin. Write an object to check for that
>regularly, and chances are you'll be in, if the object has got a hole.
Well, maybe, but if it always sets its euid before doing anything, it is
a mute point.
MikeB
Keith.
(Quaryon@Exodus)
--
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
"Mummy was an asteroid, Daddy was | Keith Marshall
a small, non-stick kitchen utensil..." | pcx...@unicorn.nott.ac.uk
>>In a call_out(), this_player(1) normally is 0. However, add_action()
>>does the trick.
>
>Not true, I just tested this on a 0.9.20 driver. this_player(1) ==
>this_player() in a call_out. Maybe this is not true with 3.2.1?
I belive I can answer this one, having recently compiled both .19 and .20.
There is a #define THIS_PLAYER_IN_CALL_OUT or something like that that
determines whether this_player() is defined in call_outs. So you're both
right! ;)
Nevertheless, security starts with a good backup system as well as
being familiar with the people to whom you trust with access. Most
security breaches will come from people who already have *some* degree
of access. The moral is do not promote people willy-nilly.
Many people stop there, thinking that some minimal UID security is
enough to keep all but those who are determined to cause trouble out.
The fact is, UID security on the TMI-2 mudlib and Nightmare Mudlibs
before 3.3 are simply security by obscurity. And the obscurity is not
all that obscure. TMI-2 is an open book. Any two-bit hacker can
crack it in less than 5 minutes. Nightmare 3.2.2 (the most secure NM
Mudlib *before* 3.3) is more secure, but still has the problem
inherent in UID security. Namely that in order to make a secure UID
system, you have to build something unbelievably complex. Nightmare
3.2.2 security is filled with configuration files which specify exact
access going to which objects get them. It is so complex, that if you
get one little thing wrong, you have created a huge hole in your
mudlib.
Since MudOS 0.9.20, MudOS has allowed people to junk UIDs. In fact,
since a long, long time ago, MudOS has supported an alternative method
of security (in the past you had to use it on top of UID security).
When the MudOS v21 driver is released, you will have proper function
pointers which will allow for stack based security to be done
efficiently and securely.
Ellery pointed out in his post what he thought was wrong with UID
security. Though I do not share his belief that UID security is
inherently evil, I do believe that a secure UID system is way too
complex to manage for a MUDding environment. I never fully understood
Ellery's security theory, as the part I read was all heavily based in
set theory, and I suck at math. So I came up with my own security
system which borrowed the concept of unguarded (which I did
understand). I also owe a lot to conversations with him, Zellski, and
Cygnus in coming up with this system.
At any rate, there are 3 types of permissions with the Nightmare 3.3 and
Foundation I Mudlibs:
1) apply
2) write
3) read
Apply is just a short way of saying call other. Write permission is
naturally that granted when an object wants to write to a file. Read
permissions when an object wants to read from a file. Permission is
granted or denied on a per-operation basis, as opposed to UID security
where permission is granted or denied on a per-operator basis. In
order for an operation to be permitted, every object involved in that
operation must be allowed. For example, when I try to rm a file:
I issue the rm command, which goes through cmd_hook() in user.c
user.c calls find_cmd() in the command daemon
user.c calls cmd_rm() in _rm.c
_rm.c removes the file
user.c and _rm.c are thw only objects involved in this operation. the
command daemon is not involved since it performs no security related
operations. In other words, it does not call any other objects or try
to read or write in this chain. In order for the file removal to be
permitted, both user.c and _rm.c must have the proper access.
Under this security system, objects have access and files have
protections. Write access to the files from which an object springs
must have a equal or higher level protection than the access that
object gets. In other words, if you have an object with global
access, only other objects which have global access should have write
access to the file from which the object is loaded. File protections
are determined based upon the directory in which a file is located.
Object access is based upon the privs string assigned to the object
when it was created. This privs string cannot be changed for the
object once it is created.
In the file removal example above, the _rm object has global access,
and so it passes the operation. For the user object, however, that
access depends on who you are. If you are an admin, you will have the
global access priv in your privs string, and thusyou will pass the
operation. If you are just some old creator, however, the operation
will fail for you (unless you are trying to rm your own file).
One immediate thing you might notice is that the rm object has global
access. With UID security systems, you are often recommended
*against* giving global access to objects. Under this system, giving
global access to an object is not at all problematic so long as only
objects with global access have write access to the files from which
those objects come.
This system, as described so far, fails when you want to grant limited
access to an object to do something greater than its normal privs,
like save mail or perhaps read a help file from a creator's home
directory. This is where Ellery's concept of unguarded access comes
in handy. Any object may perform any operation based solely upon its
access, ignoring the access of all other objects. Indeed, what you
are doing is intentionally circumventing your security system in order
to perform a certain task. The interesting thing is, with stack
security, in order to perform insecure operations, you must
specifically specify where you want to perform them. With UID
security, you have to specify where you want to perform secure
operations. In other words, UID operations are insecure by default,
stack operations on the other hand are secure by default.
unguarded simply means that if I rm a file and I do it unguarded, the
master object will only check to see that *I* have access to rm the
file. It will not check the other objects in the stack. One example
of where this is used is in the mailer, which does:
unguarded((: save_object,"/secure/adm/save/postal/d/descartes/new.o" :));
This calls save_object(), but it does it as an insecure operation,
meaning that it only checks to see if the mailer daemon can write to
that file.
At any rate, this is a solution I have come up with after having
spoken with some very security-smart people. I am sure there are
other equally secure approaches.
--
George Reese (bo...@imaginary.com) http://www.winternet.com/~borg/
phone/fax: (612) 829-5495 ftp://ftp.imaginary.com/users/borg
"No one ever conquered Wyoming from the left or from the right."
-Camper Van Beethoven
: Not true, I just tested this on a 0.9.20 driver. this_player(1) ==
: this_player() in a call_out. Maybe this is not true with 3.2.1?
*faint*. You don't mean that, do you? Whoever put that feature in MudOS
should be taken out and ... err ... prayed for. It doesn't make sense,
anyway. this_player(), yes. this_player(1), no.
: Well, use a macro or sefun then. Your alternative is make no access
: the default I assume. I would be interested in hearing some discussion
: on the feasibilty, flexibilty and performance of such a system.
Hmm, there are alternatives. Like my stackbased security system. It's
easier to user, much harder to accidentally put holes in, more flexible
and the performance is more than acceptable.
: >Why? Any command that assumes the euid of previous_object() will sooner
: >or later take on that of an admin. Write an object to check for that
: >regularly, and chances are you'll be in, if the object has got a hole.
: Well, maybe, but if it always sets its euid before doing anything, it is
: a mute point.
Not necessarily a mute point. Imagine a command that has an auxiliary
function which by accident is not static. Directly after an admin used
it, calling the auxiliary function (with his euid) is child's play.
That actually happened, you know.
Reimer Behrends
> Just pointing out here guys: If anyone wanted to break into a MUD you
> are giving them lots of information as to how to do so over the
> networks - perhaps you people should take this topic to email?
That would be like saying the comp.unix.* groups should never discuss
the internals of the kernel because it might give someone "compromising"
information.
The whole point is to put a spotlight on all the bugs and flaws in the
current LP security setup so someone can work towards a better solution.
I've wanted to implement a more typical unix-ish UID/GID system for LPC
but can't quite find a way to do it that wasn't horribly expensive at
run-time.... or needed to be manually "sync'd" with the real filesystem
underneath. IE: keeping a mapping of inode permissions would be
relatively fast (if mappings are indeed hashed properly), but it sucks
a fair amount of memory and would be out of sync if someone vi'd a file
outside the mud.
> Keith.
> (Quaryon@Exodus)
> --
> ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> "Mummy was an asteroid, Daddy was | Keith Marshall
> a small, non-stick kitchen utensil..." | pcx...@unicorn.nott.ac.uk
--
______ ___ __ ___ /* Have you been mislead? Get Linux NOW!
/ _/ / / _ )___ / |/ / Before Bill re-opens the Gates of Hell! */
_/ // _ \/ _ / -_) /|_/ / #include <std/disclaimer.h>
/___/_.__/____/\__/_/ /_/ puts("Chris Meshkin, mes...@sol.cs.wmich.edu");
Do we really want to discuss the security by obscurity argument?
Gudu
Nice post, George.
Where is this set theory based security document by Ellery? I'd like
to read it.
Gudu
In article <3belnr$7...@brachio.zrz.tu-berlin.de>,
Reimer Behrends <behr...@buran.fb10.tu-berlin.de> wrote:
>Michael Bresnahan (gu...@icicle.winternet.com) wrote in reply to my post:
>: >In a call_out(), this_player(1) normally is 0. However, add_action()
>: >does the trick.
>
>: Not true, I just tested this on a 0.9.20 driver. this_player(1) ==
>: this_player() in a call_out. Maybe this is not true with 3.2.1?
>
>*faint*. You don't mean that, do you? Whoever put that feature in MudOS
>should be taken out and ... err ... prayed for. It doesn't make sense,
>anyway. this_player(), yes. this_player(1), no.
I see no problem with it if you are aware of the behavior. It keeps a lot
of code from breaking do to this_player(1) returing 0.
>: Well, use a macro or sefun then. Your alternative is make no access
>: the default I assume. I would be interested in hearing some discussion
>: on the feasibilty, flexibilty and performance of such a system.
>
>Hmm, there are alternatives. Like my stackbased security system. It's
>easier to user, much harder to accidentally put holes in, more flexible
>and the performance is more than acceptable.
I'd like to fool around with such a scheme sometime.
>: >Why? Any command that assumes the euid of previous_object() will sooner
>: >or later take on that of an admin. Write an object to check for that
>: >regularly, and chances are you'll be in, if the object has got a hole.
>
>: Well, maybe, but if it always sets its euid before doing anything, it is
>: a mute point.
>
>Not necessarily a mute point. Imagine a command that has an auxiliary
>function which by accident is not static. Directly after an admin used
>it, calling the auxiliary function (with his euid) is child's play.
>That actually happened, you know.
Well, like i said, if you always set the euid before doing anything,
its a mute point. It would be silly to set the euid in the function
above the auxillary function that actaully does the duty. But, yes,
there is always room for human error. I usually try to surround the
secure call with seteuid()'s like this:
seteuid( geteuid( previous_object()));
str = read_file( file);
seteuid( getuid());
MikeB
: > Just pointing out here guys: If anyone wanted to break into a MUD you
: > are giving them lots of information as to how to do so over the
: > networks - perhaps you people should take this topic to email?
: That would be like saying the comp.unix.* groups should never discuss
: the internals of the kernel because it might give someone "compromising"
: information.
Agreed. If the whole exists, you should change it, not ignore it.
: I've wanted to implement a more typical unix-ish UID/GID system for LPC
: but can't quite find a way to do it that wasn't horribly expensive at
: run-time.... or needed to be manually "sync'd" with the real filesystem
: underneath. IE: keeping a mapping of inode permissions would be
: relatively fast (if mappings are indeed hashed properly), but it sucks
: a fair amount of memory and would be out of sync if someone vi'd a file
: outside the mud.
One thing I fail to understand is the UNIX is god concept. UNIX
security is neither the symbol of security perfection nor is it a
useful paradigm for the LPMud world. LPMuds are object oriented
environments where those object operate on each other as well as upon
files. The UNIX environment is one where processes operate on files.
Processes in the UNIX sense are not remotely analagous to LPC objects,
so apply a security solution for the one does not relate to the other.
You need to find a solution which works for objects operating on other
objects and files. Only a system which deals with all objects
involved in a given operation fits with the LPMud environment. A UNIX
solution is the wrong answer, and it takes a hell a lot of overhead to
support that wrong answer.
: In article <3belnr$7...@brachio.zrz.tu-berlin.de>,
: Reimer Behrends <behr...@buran.fb10.tu-berlin.de> wrote:
: >: Not true, I just tested this on a 0.9.20 driver. this_player(1) ==
: >: this_player() in a call_out. Maybe this is not true with 3.2.1?
: >
: >*faint*. You don't mean that, do you? Whoever put that feature in MudOS
: >should be taken out and ... err ... prayed for. It doesn't make sense,
: >anyway. this_player(), yes. this_player(1), no.
: I see no problem with it if you are aware of the behavior. It keeps a lot
: of code from breaking do to this_player(1) returing 0.
I see a serious problem with it. this_player(1) returns the
interactive object whose command started the process. In a
call_out(), there is no such object. Having it return an object is
insane and does not fit what the function means.
: >Hmm, there are alternatives. Like my stackbased security system. It's
: >easier to user, much harder to accidentally put holes in, more flexible
: >and the performance is more than acceptable.
: I'd like to fool around with such a scheme sometime.
Take a look at the Foundation Mudlib.
: >: >Why? Any command that assumes the euid of previous_object() will sooner
: >: >or later take on that of an admin. Write an object to check for that
: >: >regularly, and chances are you'll be in, if the object has got a hole.
: >
: >: Well, maybe, but if it always sets its euid before doing anything, it is
: >: a mute point.
: >
: >Not necessarily a mute point. Imagine a command that has an auxiliary
: >function which by accident is not static. Directly after an admin used
: >it, calling the auxiliary function (with his euid) is child's play.
: >That actually happened, you know.
: Well, like i said, if you always set the euid before doing anything,
: its a mute point. It would be silly to set the euid in the function
: above the auxillary function that actaully does the duty. But, yes,
: there is always room for human error. I usually try to surround the
: secure call with seteuid()'s like this:
: seteuid( geteuid( previous_object()));
: str = read_file( file);
: seteuid( getuid());
: MikeB
First, you completely miss Ellery's point. Yes, if coded properly,
this system using euid/uid's *can* be secure. The effort required to
create such a system is worse than the effort necessary to create a
bug-free mudlib. In the above example, in order to prevent someone
from getting access they should not have, you have to specifically
code security into the operation. Any failure to do so, or a mistake
in which you do not do so properly, and you have a security leak. Now
do this for every function.
In a stack-based system, on the other hand, any operation must go
through security checks in order to happen. You need not stick them
in. If you do not want them, then you may take such action, but in
that circumstance, you have specifically chosen to do so.
In the read_file() example above, what happens if I eval out in the
read_file() operation and leave the object with the old euid?
Take this example code:
void foo(string str) { rm(str); }
In a uid system, if this object has global access, you have just built
yourself a security hole. In a stack system, this object is not a
security hole. Now, you may argue you would never create an object
with global access that creates a free rm. But, that means every time
you write an object with global access, you have to be on careful
guard to make sure any write operations are preceeeded and followed by
euid checks, changes, and so forth. With stack security, a completely
open rm() call like the one above is no problem. You cannot make the
mistake of writing an insecure operation, unless you intentionally do
so. To make this insecure, you would have to do:
void foo(string str) { unguarded((: rm, str :)); }
Which one would figure you know what you are doing and why when you do
such a bizarre thing.
Where would one find this mudlib? I've never heard of it.
I don't think you gain as much as you make it out to be. There are
really not that many places in our mudlib that require extra safety.
>In a stack-based system, on the other hand, any operation must go
>through security checks in order to happen. You need not stick them
>in. If you do not want them, then you may take such action, but in
>that circumstance, you have specifically chosen to do so.
Yes, you explained all this in your previous post.
>In the read_file() example above, what happens if I eval out in the
>read_file() operation and leave the object with the old euid?
Nothing of consequence. When an object attempts to call the function
again, the euid will be reset.
>Take this example code:
>
>void foo(string str) { rm(str); }
>
>In a uid system, if this object has global access, you have just built
>yourself a security hole. In a stack system, this object is not a
>security hole. Now, you may argue you would never create an object
>with global access that creates a free rm. But, that means every time
>you write an object with global access, you have to be on careful
>guard to make sure any write operations are preceeeded and followed by
>euid checks, changes, and so forth. With stack security, a completely
>open rm() call like the one above is no problem. You cannot make the
>mistake of writing an insecure operation, unless you intentionally do
>so. To make this insecure, you would have to do:
>
>void foo(string str) { unguarded((: rm, str :)); }
>
>Which one would figure you know what you are doing and why when you do
>such a bizarre thing.
Yes, yes. I just need to play with it before I'm convinced the extra
security doesn't come with extra headaches. I don't like security
that gets in the way of production. If a stack based system can
achieve all it claims to, and be relatively headache free and
efficient, I'm all for it.
MikeB
Well also, we had little choice before previous_object( n) came on the seen.
Gudu
: >Take a look at the Foundation Mudlib.
: Where would one find this mudlib? I've never heard of it.
: I don't think you gain as much as you make it out to be. There are
: really not that many places in our mudlib that require extra safety.
Give Ellery wiz access to your MUD.
: >Take this example code:
: >
: >void foo(string str) { rm(str); }
: >
: >In a uid system, if this object has global access, you have just built
: >yourself a security hole. In a stack system, this object is not a
: >security hole. Now, you may argue you would never create an object
: >with global access that creates a free rm. But, that means every time
: >you write an object with global access, you have to be on careful
: >guard to make sure any write operations are preceeeded and followed by
: >euid checks, changes, and so forth. With stack security, a completely
: >open rm() call like the one above is no problem. You cannot make the
: >mistake of writing an insecure operation, unless you intentionally do
: >so. To make this insecure, you would have to do:
: >
: >void foo(string str) { unguarded((: rm, str :)); }
: >
: >Which one would figure you know what you are doing and why when you do
: >such a bizarre thing.
: Yes, yes. I just need to play with it before I'm convinced the extra
: security doesn't come with extra headaches. I don't like security
: that gets in the way of production. If a stack based system can
: achieve all it claims to, and be relatively headache free and
: efficient, I'm all for it.
: MikeB
But that is the whole point! Any secure UID system is necessarily a
headache, whereas a (well-written) stack security system requires zero
maintanence. In addition, it is not extra security in the sense you
are thinking. Both UID and stack based systems have a
valid_write()/valid_read() setup and a way of assigning UID/privs
(with MudOS) on object creation. The stack system is simpler to code,
since you do not have to make room for weird exceptions like you do
with UID basic security. With stack security, once you have done
that, you are done. With UID security, you must proceed onward to
protect each operation which must be protected. Therefore, UID
security is the bigger headache. If you have any doubts, ask anyone
who has used the Foundation Mudlib as well as any mudlib which uses
UID security as to which is easier to manage.
: seteuid( geteuid( previous_object()));
: str = read_file( file);
: seteuid( getuid());
You of course see why the above scheme can often be broken?
Reimer Behrends
Unix has multiple threads and programs with *very* limited interfaces
to outside influence. LPC code is single threaded, and programs
typically have 100s of entry points where any value can be given, and
are tied together in rather complex webs. Any attempt to simulate
Unix security in LPC is IMO doomed to failure.
It should be noted that a decent number of Unix security holes are
based on careless outside interfaces, for example the 'errors-to:'
mail hole of recent fame. It's 100x easier to introduce such a hole
into LPC code (unknowingly, of course)
-Beek
This really isn't true ... according to the docs, this_player()
returns "the interactive that caused the function to be called". The
doc is rather vague on what actually happens, but which way you see it
really depends on whether you consider call_outs to be an extension
of the same set of calls or not. It's really not, so this_player(1)
probably should be zero, but note that this_player() is really more
for game use and it's absolutely ridiculous to use it in a security
context. Having this_player() available as widely as possible makes
the writing of actual mudlib code easier; if you've ever programmed
without it you end up passing this_player() as a variable down
call_out chains alot anyway.
-Beek
==>I've wanted to implement a more typical unix-ish UID/GID system for LPC
==>but can't quite find a way to do it that wasn't horribly expensive at
==>run-time.... or needed to be manually "sync'd" with the real filesystem
==>underneath. IE: keeping a mapping of inode permissions would be
==>relatively fast (if mappings are indeed hashed properly), but it sucks
==>a fair amount of memory and would be out of sync if someone vi'd a file
==>outside the mud.
==>
==Unix has multiple threads and programs with *very* limited interfaces
==to outside influence. LPC code is single threaded, and programs
==typically have 100s of entry points where any value can be given, and
==are tied together in rather complex webs. Any attempt to simulate
==Unix security in LPC is IMO doomed to failure.
Well, I think you can implement a unixlike security system if your driver
is flexible enough, like DGD. (*poke beek*) However, it won't be trivial,
neither do I know if it is really wanted situation for a mud.
Several ways to implement (like putting the permissions on the first
like of the file - transparant from inside the mud) fail when admins
can create/edit files from outside the mud, or if wizards can ftp files.
A few posts about this can be found on The Pattern (129.16.50.30 6047).
Abigail