lima-1.0b5/
lima-1.0b5/driver/
lima-1.0b5/driver/ChangeLog.old/
lima-1.0b5/driver/Win32/
lima-1.0b5/driver/compat/
lima-1.0b5/driver/include/
lima-1.0b5/driver/testsuite/
lima-1.0b5/driver/testsuite/clone/
lima-1.0b5/driver/testsuite/command/
lima-1.0b5/driver/testsuite/data/
lima-1.0b5/driver/testsuite/etc/
lima-1.0b5/driver/testsuite/include/
lima-1.0b5/driver/testsuite/inherit/
lima-1.0b5/driver/testsuite/inherit/master/
lima-1.0b5/driver/testsuite/log/
lima-1.0b5/driver/testsuite/single/
lima-1.0b5/driver/testsuite/single/tests/compiler/
lima-1.0b5/driver/testsuite/single/tests/efuns/
lima-1.0b5/driver/testsuite/single/tests/operators/
lima-1.0b5/driver/testsuite/u/
lima-1.0b5/driver/tmp/
lima-1.0b5/etc/
lima-1.0b5/lib/WWW/help/
lima-1.0b5/lib/cmds/
lima-1.0b5/lib/cmds/create/
lima-1.0b5/lib/cmds/player/attic/
lima-1.0b5/lib/contrib/bboard/
lima-1.0b5/lib/contrib/boards/
lima-1.0b5/lib/contrib/marriage/
lima-1.0b5/lib/contrib/roommaker/
lima-1.0b5/lib/contrib/transient_effect/
lima-1.0b5/lib/daemons/channel/
lima-1.0b5/lib/daemons/imud/
lima-1.0b5/lib/data/
lima-1.0b5/lib/data/config/
lima-1.0b5/lib/data/links/
lima-1.0b5/lib/data/news/
lima-1.0b5/lib/data/players/
lima-1.0b5/lib/data/secure/
lima-1.0b5/lib/domains/
lima-1.0b5/lib/domains/std/2.4.5/maze1/
lima-1.0b5/lib/domains/std/2.4.5/npc/
lima-1.0b5/lib/domains/std/2.4.5/post_dir/
lima-1.0b5/lib/domains/std/2.4.5/sub/
lima-1.0b5/lib/domains/std/camera/
lima-1.0b5/lib/domains/std/config/
lima-1.0b5/lib/domains/std/cult/
lima-1.0b5/lib/domains/std/effects/
lima-1.0b5/lib/domains/std/misc/
lima-1.0b5/lib/domains/std/monsters/
lima-1.0b5/lib/domains/std/recorder/
lima-1.0b5/lib/domains/std/rooms/
lima-1.0b5/lib/domains/std/rooms/beach/
lima-1.0b5/lib/domains/std/rooms/labyrinth/
lima-1.0b5/lib/domains/std/school/
lima-1.0b5/lib/domains/std/school/O/
lima-1.0b5/lib/domains/std/spells/
lima-1.0b5/lib/domains/std/spells/stock-mage/
lima-1.0b5/lib/domains/std/spells/stock-priest/
lima-1.0b5/lib/help/
lima-1.0b5/lib/help/admin/
lima-1.0b5/lib/help/hints/General_Questions/
lima-1.0b5/lib/help/hints/Pirate_Quest/
lima-1.0b5/lib/help/player/
lima-1.0b5/lib/help/player/bin/
lima-1.0b5/lib/help/player/quests/
lima-1.0b5/lib/help/wizard/
lima-1.0b5/lib/help/wizard/coding/guilds/
lima-1.0b5/lib/help/wizard/coding/rooms/
lima-1.0b5/lib/help/wizard/lib/daemons/
lima-1.0b5/lib/help/wizard/lib/lfun/
lima-1.0b5/lib/help/wizard/lib/std/
lima-1.0b5/lib/help/wizard/mudos_doc/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/interactive/
lima-1.0b5/lib/help/wizard/mudos_doc/applies/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/concepts/
lima-1.0b5/lib/help/wizard/mudos_doc/driver/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/arrays/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/buffers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/compile/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/filesystem/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/floats/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/functions/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/general/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mappings/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/mixed/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/numbers/
lima-1.0b5/lib/help/wizard/mudos_doc/efuns/parsing/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/constructs/
lima-1.0b5/lib/help/wizard/mudos_doc/lpc/types/
lima-1.0b5/lib/include/driver/
lima-1.0b5/lib/log/
lima-1.0b5/lib/obj/admtool/
lima-1.0b5/lib/obj/admtool/internal/
lima-1.0b5/lib/obj/admtool/mudinfo/
lima-1.0b5/lib/obj/admtool/secure/
lima-1.0b5/lib/obj/secure/
lima-1.0b5/lib/obj/secure/cmd/
lima-1.0b5/lib/obj/secure/mailers/
lima-1.0b5/lib/obj/secure/shell/
lima-1.0b5/lib/obj/secure/shell/classes/
lima-1.0b5/lib/obj/tasktool/
lima-1.0b5/lib/obj/tasktool/internal/
lima-1.0b5/lib/open/
lima-1.0b5/lib/secure/
lima-1.0b5/lib/secure/cgi/
lima-1.0b5/lib/secure/modules/
lima-1.0b5/lib/secure/simul_efun/
lima-1.0b5/lib/std/adversary/
lima-1.0b5/lib/std/adversary/advancement/
lima-1.0b5/lib/std/adversary/armor/
lima-1.0b5/lib/std/adversary/blows/
lima-1.0b5/lib/std/adversary/death/
lima-1.0b5/lib/std/adversary/formula/
lima-1.0b5/lib/std/adversary/health/
lima-1.0b5/lib/std/adversary/pulse/
lima-1.0b5/lib/std/adversary/wield/
lima-1.0b5/lib/std/classes/event_info/
lima-1.0b5/lib/std/container/
lima-1.0b5/lib/std/living/
lima-1.0b5/lib/std/modules/contrib/
lima-1.0b5/lib/std/patterns/
lima-1.0b5/lib/std/race/
lima-1.0b5/lib/std/race/restricted/
lima-1.0b5/lib/std/room/
lima-1.0b5/lib/tmp/
lima-1.0b5/lib/trans/
lima-1.0b5/lib/trans/admincmds/
lima-1.0b5/lib/trans/obj/
lima-1.0b5/lib/wiz/
LIMA MUDLIB SECURITY

[ ed. this is a work in progress.  It is more a description of the
  system rather than how to run the system.  Working with the
  "admtool" program and the background presented here should help
  quite a bit.  As always, come to Lima Bean for more details, but
  ensure that you've read this whole document before asking questions
  about security :-) ]

There are many elements that work together to create the security
system with a Lima mud.  Here are the various pieces that you need to
be aware of to fully understand Lima's security system:

- SECURE_D
- privileges
- protections
- stack-based security concepts
- the user object
- wizards
- domains
- admins
- the unguarded() function
- other security-related functions

This document will also cover how to maintain a secure mud and how to
look for holes in your security.  One of the benefits of the Lima
security system is a simple and direct way to look for problems.


SECURE_D

This daemon, located at /secure/daemons/secure_d.c is the heart of
the Lima security system.  It manages the core set of data that is
responsible for maintaining proper security.

At a high level, it maintains the follow items of information:

- what privileges exist, and who "has" those privileges
- what domains exist, and who is within those domains (and who are
  the "lords" of a domain)
- what protections have been applied to directories
- which users are wizards

The daemon defines functions for manipulating, querying, and verifying
these pieces of information.  There are a few support files to provide
additional high level capability; these are M_ACCESS (which is the
/secure/modules/access.c file) and /secure/simul_efun/security.c.

In standard practice, you will never need to interact directly with
the SECURE_D, but rather through the interface functions in M_ACCESS
and the sefuns.  These functions are summarized later.


PRIVILEGES

The security revolves around defining and using sets of privileges.
These privileges are assigned to objects (and via a user object, to a
particular user when they connect to the mud).  Privileges are applied
to directories, where they are called "protections".  A particular
object must have enough privilege to overcome the protection on a
directory before it can read/write to that directory.

In its simplest form, a privilege is a string of alphabetic, lower
case characters.  There are few additional forms that are used:

1) A lower case alphabetic string, followed by a colon, optionally
   followed by another lower case alphabetic string.

   For example: deathblade:objects

2) Simple privileges or those with a colon in them (as in (1)) that
   have their first letter capitalized.

3) An @ character followed by alphabetic characters.  For example:
   @secure.

4) The integers 0 and 1.

Now, a discussions of the privilege heirarchy and ordering among them
is needed, before the above forms can be fully understood.

1 is the "highest" privilege of all.  All other privileges are "lower"
than this privilege.  Comparisons are made often between privileges to
determine if a given privilege is equal to or higher than another.  1
will always be higher (or equal).

0 is the lowest privilege of all.  It is the exact opposite of 1.  All
other privileges are (equal to or) higher than 0.

Named privileges are, orderwise, between 0 and 1.  Two named
privileges are neither higher nor lower than each other.  They are
"disjoint" and a comparison will always fail.

A small graphic portrays this very well:

          1
         /|\
        / | \
       /  |  \
    foo  bar  @blah
       \  |  /
        \ | /
         \|/
          0

This graphic shows how 1 is the highest, foo/bar/@blah are between 1
and 0 (but have no order compared to each other), and all are higher
than 0.

Privileges may have "subprivileges".  The subprivileges have a colon
after their "owner privilege", possibly followed by a subprivilege
name.  The privileges and subprivileges are organized as follows:

   1  -->  foo  -->  foo:  -->  foo:bar  ->  foo:barlonger -> 0
                           -->  foo:blatz  ->  0

The privilege "foo" is called the "control privilege".  Having that
privilege allows you to define the subprivileges.  It is not as high
as 1, but is higher than all subprivileges of itself.  The "foo"
subprivileges are called "data privileges".  The "foo" privilege may
also be called an "owner privilege", as it "owns" the subprivileges.

Privileges such as "foo" are created when the user "foo" becomes a
wizard (the "foo:" subprivilege is also defined at wizard creation
time).  There is a one to one correspondence between wizards and these
privileges; there is no way to create these control privileges except
through creating wizards.  Take note that wizard privileges are
entirely in lower case.  The wizards "user object" will receive their
privilege when they connect to the mud (see the USER OBJECT section).
As it is the control privilege, the wizard may define subprivileges as
needed.  Why this is handy will be discussed in a bit.

Administrators may also define privileges that begin with "@".  These
can be applied as protections to a directory or as the privilege of a
certain object.  Note that it is impossible for a wizard to have a
name beginning with "@", so it would not be possible to spoof the
privilege.  As with wizard-based privileges, subprivileges may be
created as needed.

Lastly, there are domain privileges.  These are automatically created
when a domain is created.  They are like wizard privileges, but have
their first letter capitalized (thus preventing a wizard spoofing the
privilege).  When a domain is created, two associated privileges are
created for it: the control privilege and a data privilege.  For
example, let's say you create the domain "telaria".  The two
privileges that are created are: "Telaria" and "Telaria:".

Now comes the complicated part.  Providing additional privileges to a
wizard.  This is done by linking parts of the privilege graph
together.  This definitely calls for a graphic:

           1
          / \
         /   \
       joe   sue
        | \   |
        |   \ |
       joe:  sue:
         \   /
          \ /
           0

The wizard Joe now has access to directories with that are protected
by the "sue:" privilege.  Note that you do not want to give the
control privilege "sue" to Joe, because that would allow him to
create, remove, or alter subprivileges of the wizard Sue.  He could
even use the control privilege to allow other wizards access to Sue's
directories.

One other caveat: when the security system is considering whether an
object has the privilege to access something protected by "sue:", it
will require that the object has a privilege higher than "sue:" ("sue"
or 1), or that the object's privilege is on the list of privileges
"sue:" has been opened up to.  Note that it requires these assigned
privileges to match exactly.  Therefore, even if the "joe:" privilege
was added to the list for "sue:", the "joe" privilege won't match
(even though it "should" since it is higher than "joe:").

For this reason, when assigning privileges to another (like "joe" to
"sue:"), you need to consider what objects will need to access the
protected directories (protected by "sue:" in this example).  In this
example, We want the wizard Joe to have access, so we use Joe's
privilege.  If we wanted one of Joe's objects (typically "joe:" or
another subprivilege of that) to access "sue:" then we would open that
specific subprivilege.

Lastly, any privilege can be opened to any other.  Typically, these
capabilities will be used to provide a wizard access to specific parts
of the mudlib.  Domains are a more general mechanism, so investigate
using that before considering the opening of specific privileges.


PROTECTIONS

A directory can have a privilege associated with it.  Any objects
within that directory are limited to that as a maximum privilege
attainable.  In addition, an object must have at least that privilege
to write into the directory.

Directories can also have read privileges assigned to them to limit
what objects can read files in the directory.


STACK-BASED SECURITY CONCEPTS

The Lima security system uses a method called "stack-based security".
Simply put, when a privileged operation needs to occur, every object
on the current execution stack will be examined to ensure that all of
them have the necessary privileges to perform the operation.

As an example, let's say that the wizard Joe wants to write a file
into Sue's directory (/wiz/sue).  We'll also say the directory has a
protection of "sue:" (which Joe has had opened up to him).  Joe will
use a command called "writefile" which is in a directory that has a
protection of 1.

Okay: when Joe uses the command, the stack might have the following
objects on it:
   /secure/user    priv=joe
   /bin/writefile  priv=1
   /secure/master  priv=1    (the valid_write() call)

valid_write() is going to verify that the current privilege is enough
to overcome the protection of "sue:" on the /wiz/sue directory.
SECURE_D is called to check the privilege.  It steps backwards through
the stack, finding privilege 1 a couple times.  This is higher than
"sue:" so SECURE_D continues back through the stack.  It finds the
/secure/user object and that it has privilege "joe".  "joe" is on the
access list for "sue:" so it passes.  The stack has been verified, so
the SECURE_D makes one final check and verifies that this_user() has
enough privileges.  this_user() is Joe, with the "joe" privilege, and
it, again, succeeds.  The result is that Joe and the writefile command
are allowed to write into /wiz/sue.

For another example, let's say that the wizard John tries to write
into Sue's directory with the writefile command.  When the stack is
checked, it would find the privilege "john" which is NOT open to
"sue:" and so the operation would be denied.

Lastly, how about the case where an evil wizard tries to code an
object that calls writefile.  He then gives that to an admin, hoping
that the admin's privileges would allow the operation to succeed.  A
simple examination of the stack reveals this won't happen, though:
   /secure/user     priv=1   (the admin)
   /wiz/evil/mytoy  priv=evil:  (wiz directories are always name:)
   /bin/writefile   priv=1
   /secure/master   priv=1

As soon as the stack system saw the "evil:" it would cause the
operation to fail.  It is also very interesting to note, now, that the
evil wizard doesn't have any privileges higher than "evil" and,
therefore, cannot place his mytoy object into a priv=1 directory
(causing it to pass inspection).


THE USER OBJECT

As you may have seen from the discussions above, the user object is
rather central to the security system.  It defines the privilege that
a particular user has, and therefore limits their operations.

When a user connects to the mud and logs in successfully, the user
object will then be assigned a privilege.  For players, this will be 0
(no privileges at all).  From there on, players are restricted to only
the most basic of operations.  Wizards will be assigned their own
privilege (the privilege named after them).  Admins will receive
privilege 1 (the highest).  The SECURE_D is quite involved in
determining what a user actually is (player, wizard, admin); the user
object will use that information to decide on the privilege.

Note that the /secure/user object is tightly locked up to prevent any
potential alteration of its privilege or faking out what it will be
set to.


WIZARDS

The SECURE_D daemon records which users have been marked as wizards.
There is no other record of this fact, so there is no other way to
become a wizard, other than convincing the SECURE_D that you are one.
Of course, to convince it of this, you must be an admin (which implies
you are a wizard already).

There is a simul-efun named wizardp() that should be used to detect if
somebody is a wizard.  This helps isolate you from the details of how
to ask the SECURE_D and it also provides a bit more flexibility.
wizardp() takes a single argument: a user object, or a string
containing the user's name in lower case.

When a wizard is created, their associated privilege is created along
with the subprivilege "name:" (where <name> is the wizard's name).  If
the wizard will also receive a directory, then that directory has the
"name:" protection applied to it.  It is very important that the
subprivilege is used for the directory.  You do not want the wizard's
objects to have the control privilege!  Only the wizard himself should
have that privilege.


DOMAINS

Domains are a mechanism that simplifies assigning (groups of)
privileges to wizards and protections to directories.

Also, since the creation of domains and the inclusion of members in
those domains is tightly controlled by SECURE_D, domains and their
membership can be trusted in all contexts.  This trust is used in
certain parts of the mudlib to classify wizards and the commands and
options available to them.  More on this later.

Each domain has a name, a set of "domain lords", and a set of members.
Typically, it will also have a directory associated with it, such as
/domains/name/.  The name must be lower case, alphabetic characters.
The domain lords are the wizards who allow/revoke membership in the
domain and have the control privilege for the domain.  The domain
members simply have domain data privileges.


ADMINS

Admins are defined solely by being a member of the "admin" domain.
Members of this domain will have privilege 1 assigned to their user
object, meaning they have the highest possible privilege.


THE UNGUARDED() FUNCTION

An obvious problem appears with a stack-based security system.  How is
it possible for a player (with privilege 0) to cause entries to appear
in the /log directory (protection 1) ?  The short answer is that it is
impossible for a user to reach privilege 1.  On the other hand, if the
player can ask a high privilege object to do it for him and then
somehow avoid being seen in the stack, then it would work.

The unguarded() function is used for just this purpose.  When an
object uses unguarded(), then the call stack tracing used by the
security system stops at that point and ignores all previous callers.
A simple example should demonstrate:

   /secure/user            priv=0   the player
   /secure/daemons/log_d   priv=1   theoretical logging daemon
   unguarded()
   /secure/daemons/log_d   priv=1   func used in the unguarded()
   /secure/master          priv=1

In this example, the security system will only look back as far as the
unguarded(), ignoring that the prior object had a privilege of 0.

Now, the one big, major, super-duper, never forget, tattoo it to your
forehead, inscribe on your toes, end-all of all rules:

--> Use unguarded() VERY VERY carefully <--

Why?  unguarded() is the ONLY (*) way that you can create a security
hole in your mudlib.  This is by virtue of the fact that this is how a
low-privilege user can gain a high-privilege status to execute certain
functions.  Therefore, you must take particular care to how it is
used.  The parameters to unguarded are:

unguarded(mixed privilege, function func)
  Evaluate the specified function at the specified privilege

The function that is passed to unguarded() should be extremely limited
and if it is parameterized at all, ensure that your parameters are
validated.  For example:

  unguarded(1, (: save_object, SAVE_FILE :));

This is the "best" kind of use of unguarded(): the function does one
thing and one thing only.  The file that it uses is hard-coded.
Another example:

  unguarded(1, (: write_file, LOG_DIR + fname, contents :))

This is tricky: before using something like this, you must ensure that
somebody cannot get this line of code to be executed with an fname of
something like "../data/secure/access.o".  So... before this is
called, it would be very important to ensure fname is one of a few
legal values (/std/reporter.c does something like this) or that it
contains no "." characters in it (or alternatively, only alphabetic
characters).

Here is an absolute WORST thing to do. This is the perfect security
hole:

void security_hole(string fname, string contents)
{
  unguarded(1, (: write_file, fname, contents :));
}

This allows anybody to write anything to any file in any directory.
Generally, not a good idea. :-)

Therefore, it is usually a good idea to scan your mudlib every now and
then and examine your uses of unguarded() to see if there any are
potential holes.  The Lima Team does this, so unless you are coding
with unguarded() yourself (shouldn't be necessary), then you won't
have to worry about this.

(*) Okay: technically, there are a couple other ways you can create a
security hole.  One is to have a high privilege object inherit a low
privilege object.  Second is to have a high privilege object #include
a low privilege file.  A simple example: let's say /secure/user did a
#include "/tmp/somefile.h".  Any Joe Wizard could rewrite somefile.h
to include the security_hole() function from above.  This would then
become part of your high security object, and is then usuable by
anybody to execute high privilege operations.

At this point, the Lima Team investigating a couple ways to prevent
the inherit/include problem to avoid accidents on the behalf of admins
(note that only admins can edit priv 1 objects and thereby create this
hole).


OTHER SECURITY-RELATED FUNCTIONS

check_privilege()             [ SIMUL_EFUN ]
check_previous_privilege()    [ SIMUL_EFUN ]
get_protection()              [ SIMUL_EFUN ]
get_privilege()               [ SIMUL_EFUN ]
query_domain_members()        [ SECURE_D ]
query_domains()               [ SECURE_D ]
set_privilege()               [ M_ACCESS ]


THE ADMTOOL SYSTEM


GLOSSARY

admin
administrative privilege
control privilege
data privilege
domain
domain privilege
owner privilege
privilege
protection
subprivilege
wizard
wizard privilege