
ACCESS CONTROL LISTS FOR CVS (Proposal)
========================================

This document discusses the design and implementation of ACL (Access
Control Lists) for the cvs-nserver.

This document is open for comments, suggestions and modifications.

You can commit new revisions of it into CVS after discussion in
cvs-nserver-devel mailing list (available on
http://sf.net/projects/cvs-nserver/).

$Id: acl-rfc.txt,v 1.1.2.7 2002/03/31 18:22:13 tyranny Exp $


Primary Authorization Features
===============================

The concept of "no ACL" is used a lot, and this could sometimes lead
to (unexpected) opening of repository to extemely wide access.  In
short, when the ACL file is non-existent or becomes empty after
revoking all privileges granted, the "default ACLs" come into play.
Default ACLs simply allow everything.  Maybe we need some flag file in
the repository that means "default ACLs is deny everyone".


We need the following basic authorization features:

- the ACLs are assigned on per-file-on-branch, not on per-file basis.
  Same goes for directories case.

  Rationale: a very common task is "let support engineers commit
  patches to the stable branch, and not to the trunk, which is for
  main developers only";

  NB: In CVS you cannot do anything with a "file" (or directory) per
  se.  Even when you delete a file, you delete it only on a specific
  branch.  Although, file-level ACLs could be emulated with default
  directory ACLs, see below.

  This should be considered from the very first days of development.
  Maybe it would not be implemented right from the start, but
  switching from "acl-per-file" to "acl-per-branch" would seem like
  hell to do.


- if there are no ACLs on a certain branch, then ACLs on a trunk are
  used instead.  If there are no ACLs on a trunk, then default
  directory ACLs for certain branch are used.  As a last resort, the
  default ACLs are used.

  This could help to emulate the "per-file" ACLs.

  Rationale: often the developers working on a trunk and developers
  fixing bugs on a stable branch are the same people, and there is no
  need to setup different ACLs.


  NB: From now on when speaking about the "file", we mean "file on a
  branch", if it's not explicitly specified otherwise.  Same for a
  directory.


- of course, user groups are fully supported.

  In general, when we are thinking about the "user", we mean "user or
  group of users", if it's not explicitly specified otherwise.


- the basic operations that should be controlled for each branch in a
  project or for a file (that is, file on a branch) are:

	- check revision out (read access);

	  Rationale: don't let somebody see what happens on the
	  development branch.

	- check revision in (write access);

	  Rationale: obviously, don't let somebody commit anything on
	  this branch;


- the basic operations that should be controlled for each directory
  are:

	- accessing the files and subdirectories in this directory;

	- modifying (checking in, adding and removing files, creating
	  directories);

  Rationale: obviously, we need to handle two simple and frequent
  cases: "give somebody read-only access to the repository", "don't
  let somebody access any files inside of a directory (with
  subdirectories)".

  Rationale: indeed, the 99% of files in each given directory have the
  same permissions.  But, sometimes we need to single out one
  important file and harden its security.


- default subdirectory ACLs.  If the directory does not have
  explicitly specified ACLs, they are taken from its parent
  directory.

  Rationale: see the convenience of "BSD groups".


- when checking the access rights, the directories are traversed from
  the top directory to the directory where the file resides;

  Rationale: obvious to anyone who has ever worked with filesystem
  permissions :) Also, this brings sane implementation of "default
  subdirectory ACLs".  Also, it greatly simplifies the recursive
  processing of repositories.


- the basic operations that should be controlled for each module are:

	- tag (non-branch) revisions (this includes moving and
	  deleting tags);

	- create branches;

  Rationale: let the Project Release Manager do her job, don't let
  mere developers to mark releases.  It doesn't seem worth the trouble
  to make "tagging" access control be on a per-file basis, because the
  keyword in "Project Release Manager" is "Project".


	- list of ACL administrators for this module, who can setup
	the permissions as they see fit.

  Rationale: why not per-directory administrators?  If there are say
  two distinct development group working on the same project, and they
  need one administrator in each group, why not let them have separate
  administrator for ./project/subsystem1 and ./project/subsystem2
  directories?  Answer: it seems better to create two modules:
  subsystem1 and subsystem2, and then to create an ampersand-module
  called 'project' that merges those two modules.

  Rationale: if administrators can setup only ACLs, and not to modify
  files in the repository, the security risks are greatly reduced.
  Surely, the attacks of kind "no one is allowed to checkin any file"
  could be tolerated "better" than attacks of kind "every file has a
  thousand junk revisions applied, and all branches are renamed to
  something funny".

  For that reason, users who are ACL administrators for this module
  could not appear in the file-level ACLs (such entries are ignored,
  and the special script could be written that checks the sanity of
  permissions in the repository).


- I don't think there is a need of concept for "owner" and "owner
  group" for each file.

  Rationale: default permissions for files in any given directory
  completely supersede the notions of owner/group.  Also, the
  administrator should be the only one, so to speak.


- there is a notion of "repository administrator", who can modify the
  list of ACL administrators for each module, and doesn't do anything
  else (mostly, at least she doesn't have root access to all the files
  in the repository).


This list is probably not complete, though I think I've covered the
essential parts.  What is needed now: you should think up a real-life
case that you think is not covered in this text, and we will think
together if that case could be a) implemented by the above; b) needs a
whole new feature; c) could be scrapped and forgotten.


Basic Semantics
================

The ACLs for a particular directory or a file are generated traversing
from the "top-level directory", i.e., repository directory, towards
the particular directory or a file.

The basic semantics is restrictive-only.  You cannot give additional
rights to user in a directory, if parent directory prohibits those
rights.  E.g., if the module directory grants only "access" rights to
user, she could not in any way get a "modify" or "checkin" rights in
that module.


We must strictly differentiate between two cases:

 - the directory itself does not have an associated Access Control
   List (i.e., there is no file DIRACL in that directory, or it is
   empty: this is rather common case);

 - the directory has an associated Acess Control List.


Repository policy
------------------

"Repository policy" is an "allow"/"deny" decision which is used when
there were no associated ACLs in all of the directories along the
particular path.  Repository policy is controlled by the
`CVSROOT/default-deny' file:

  - if this file exists (its contents is ignored), the repository
    policy is "deny";

  - if this file does not exist, the repository policy is "allow".


Directory permissions semantics
--------------------------------

Here is how the permissions are checked for the particular directory
for the particular user:

- we set the current effective permissions to 'all';

- for each of the the parent directories down from the top-level
  directory:

   - if the parent directory does not have an ACE for user, it is
     skipped;

   - if the parent directory has such an ACE, we AND-mask the current
     effective permissions with the permissions we get for that parent
     directory;

     [ E.g., those permissions could still be "all", or become
       "access", or become completely restrictive, "none".

       Note that there is no way for the effective permissions to
       become less restrictive while traversing the list of [parent
       directories. ]

- now, we have four cases: 

  - none of the parent directories did have an ACE for the user;
    target directory does not have an ACE for the user:

      we check the requested permissions against the repository
      policy;

  - none of the parent directories did have an ACE for the user;
    target directory does have an ACE for the user:

      we check the requested permissions against the target directory
      ACL;

  - at least one parent directory did mentioned user in an ACE; target
    directory does not have an ACE for the user:

      we check the requested permissions agains combined permissions
      of parent directories;

  - at least one parent directory did mentioned user in an ACE; target
    directory does have an ACE for the user:

      we check the requested permissions agains combined permissions
      of parent directories combined with permissions of target
      directory;


That's all.


What does that all mean?

Here is the short break-down of use cases:

 - user is not mentioned in ACLs alongside the traversing path at all:
   repository policy comes into effect (that's the only time it is the
   case);

 - user is mentioned in one of the parent directories: he will never
   be permitted to do besides for what was allowed in those
   directories;

 - the sequence above covers and fixes the bug found by Alex, where
   the case of "parent dirs mention user" + "target dir does not
   mention user" give the "permission denied" to him, instead of "what
   is specified in parent directory;



Quality Assurance
==================

All of the ACL-related functionality is fully unit-tested and
(hopefully) feature-tested.

Always create test-cases for new code in acl/ subdirectory; always
create test-cases for new features in cvs binary itself.  One cannot
overemphasize the importance of this.

This has already helped me a lot during the development, and I think
it'll be crucial alongside, when the functionality will be extended.


Internal Structure
===================

Internal structure of on-disk ACL-related files is documented in the
acl-on-disk.txt (soon to be written).

