cfengine

 [image of the Head of a GNU] [ English ]


Porting cfengine to Windows NT

Bjørn Gustafson - Jørgen Kjensli

Abstract:

As computer systems are becoming more complex the need to automate the tedious tasks of the system administrator increases. Cfengine is a program that does this on UNIX systems. Although Windows NT is supposed to be easy to use with its graphical user interface, it often makes administering and configuring the system very time consuming. A tool like cfengine can therefore be very useful on NT as well. This report describes the work we have done to port cfengine to Windows NT and some thoughts about future work.


Contents

                         

Introduction

This report is the result of a final year project at Oslo College, Norway, by Bjørn E. Gustafson and Jørgen Kjensli. The object of the project was to port cfengine, a site configuration engine for UNIX systems, to Windows NT. Cfengine was written by Mark Burgess and the project was conceived by him and he engaged the authors to do the job.

Cfengine is a program that is used all over the world, therefore we decided to write the report in English so that it can be available for current and future users of cfengine. The report goes into some technical detail and we assume that the reader has knowledge of C programming.

What is cfengine

Cfengine is a language based tool specifically designed for configuring and maintaining BSD and System-5-like operating systems attached to a TCP/IP network. You can think of cfengine as a very high level language-much higher level than Perl or shell: a single statement can result in many hundreds of operations being performed on multiple hosts. The main purpose of cfengine is to allow you to create a single, central configuration file which will define how every host on your network should be configured. The cfengine interpreter runs on every host on the network and parses the central file (or file-set); the configuration of each host is checked against this file and then, if you request it, any deviations from the define configuration are fixed. If such a central file had to mention every single host on a network individually, it would have to be very large, but cfengine uses a flexible system of "classes" which helps you to describe many hosts with a single statement, or make complex decisions which pick out very specific actions for specific hosts if necessary.

Why cfengine on NT

Cfengine has long been a successful tool used by system administrators all over the world. It has only been available for UNIX like systems, but many administrators have expressed the need for a program like cfengine that runs Windows NT. Windows NT is a complex system and is at least as difficult to configure and maintain as a UNIX system. We believe that if cfengine is ported to the NT platform it can become a much used tool for the administrators. For Mark it would mean that his invention will be able to control the two most widely used platforms for computer networks. For Bjørn and Jørgen it was an exciting challenge and a golden opportunity to have our names associated with an internationally known program.

The job of porting a large program like cfengine can seem quite overwhelming at first. Given the vast amount of code, we realised that re-coding the program for the NT platform would be far too much work for a project like this. Besides, it seemed unnecessary to rewrite all the code that actually worked on the UNIX platform. There had to be an easier way of doing it. So we were really presented with two problems. How can we compile  the already functional code on the NT platform, and how does NT behave in relation to the resources which cfengine controls on UNIX. In the next few sections we will present our thoughts on these two problems. Then we explain how we went about implementing our ideas. Finally we discuss some further work that can be done to make cfengine a valuable resource for Windows NT administrators.

Compiling functional code on the NT platform

We are not the first to port UNIX programs to NT. A lot of work has already been done in this area, and we were early made aware of the existence of porting layers . A porting layer is a piece of software that lies between the application to be ported and the operating system. You could say that it translates operating system calls to the native platform. In our case from UNIX system calls to Windows NT system calls. For this to work the program is compiled  with a special compiler that relays the alien function calls to the porting layer. The porting layer will in turn do what is necessary to obtain the desired effect on the NT platform. We found that there were at least four such porting layers available. We did not know how good they were or how much they would help us. We will discuss the job of choosing a porting layer later in the report.

NT in relation to cfengine

UNIX and NT are two very different operating systems. Both in the way they are used and how they work behind the scenes. We could not assume that all of cfengine's functionality on UNIX would make sense on NT. We had to do a comparison between the two platforms to see what cfengine could not do or would have to do differently on NT. How NT behaves will also be related to how good the porting layer we choose is. We need to see how much UNIX functionality it can mimic.

Overview of cfengine

Cfengine is a large program and to get an overview of its structure can be difficult. The purpose of this chapter is to give the reader the necessary understanding of cfengine to be able to follow when we later go into some detail. As we see from the file listing of the source directory, cfengine is built up of many files :
 2Dlist.c      cflex.l.in    edittools.c   item-file.c   patches.c
 Makefile.in   cfparse.y     encrypt.c     item.c        popen.c
 STRUCTURE     cfrun.c       errors.c      link.c        process.c
 acl.c         checksums.c   eval.c        locks.c       proto.c
 cf.defs.h     chflags.c     filedir.c     log.c         read.c
 cf.extern.h   classes.c     filenames.c   macro.c       repository.c
 cfd.c         client.c      globals.c     misc.c        rotate.c
 cfd.h         comparray.c   ifconf.c      modes.c       tidy.c
 cfengine.c    conf.h.in     image.c       mount.c       varstring.c
 cfkey         copy.c        install.c     net.c         wildcard.c
 cfkey.c       df.c          item-ext.c    parse.c

It is not important to know what all these files  do, but we will explain the most important ones and of course the ones we will be working with.

Before cfengine can be used on a system it has to be compiled  on that specific system. This is very important because it configures cfengine for that system, and no two systems are exactly alike. Cfengine is compiled  by running a script called configure  and then a program called make . The configure  script basically checks your system and sets some variables to define how the system is set up. It also creates makefiles, which are input files for the make  program. They tell make  which files should be compiled  and where to put the output files.

The files discussed below are the ones you will need to know about.

Choosing a porting layer

To port cfengine to NT, we need to compile  the program on an NT machine. This is only possible if the compiler  understands certain UNIX system calls (i.e. pipe, fork, etc), or if we rewrite the cfengine code. The latter option would clearly require a lot of time and effort, and since the creation of the porting layers such as UWIN , CygWin  and their likes, it is hardly necessary. What we needed to do was to find a porting layer that supported most (or all) of the calls needed by cfengine, and then code the rest by hand.

The porting layers we had to choose from was NuTCracker  from DataFocus, OpenNT  from Softway Systems, UWIN  from AT&T and CygWin  from Cygnus Solutions. The two first options were promptly discarded as they are both proprietary. UWIN  is also proprietary but is available for free non-commercial use. We were pleased to find that CygWin  is a free software solution, and we decided that if it could compete with UWIN , i.e. it supported all (or most of) the functions that UWIN  did, CygWin's  library would be our preferred choice. From the documentation of the two porting layers it seemed as though their functionality was just about the same. We therefore decided to try compiling cfengine with CygWin  and if it worked satisfactorily we would use it. If not, we would have to test UWIN  as well.

Desired functionality

In addition to supporting some basic system calls (pipe, fork, etc), the porting layer also needed to be able to execute some basic shell commands. They are listed in cfengine's src/classes.c as ps , netstat  and mount /umount .

We found that both CygWin  and UWIN  supported the ps  command (although CygWin  seemed to be a bit quicker). However, the output of the call was a listing of processes  using the porting layer. All processes  running directly under NT were omitted. This is a problem that we must solve, and we will talk about it in a later chapter.

The netstat  command worked equally well for both UWIN  and CygWin , and hopefully satisfactorily for cfengine.

Since it is impossible to mount  anything under NT, the mount  and the umount  commands are not needed. On NT all devices are mounted  automatically, but in a different sense than on UNIX. The commands seem to be supported by both porting layers, however. We will discuss in a later chapter what mounting  would mean on NT.

The easiest way to check if the porting layer supports all of the system calls (such as pipe, fork, etc) that cfengine uses would be to try to compile  the code under the porting layers with the gcc compiler  provided by the porting layers and evaluate the error messages, if any.

UNIX vs. NT

The way that UNIX operating systems work is very different to how Windows NT works. One of the basic differences that will affect cfengine is the fact that UNIX configuration is defined in plain text files  while NT stores most of this information in binary format (in the registry ). Cfengine can check and edit text files , and therefore it is easy to control the system configuration on a UNIX platform. A lot of this configuration functionality will probably be lost on the NT platform. This poses another problem - how can cfengine edit binary formats? We discuss this in a later chapter.

Another thing that works differently on the two platforms is file permissions. On UNIX you can only specify the rights of the user (owner of the file), one group and all others. NT however uses access control lists (ACLs ). You find this on some UNIX systems as well, but it is not widely used. ACLs  is a much more complex way of handling file access than the traditional UNIX style permissions. If you want to give users other than the owner access to a file on UNIX, you have two choices. You either have to give everyone access or add the users to the group linked to the file. All the members of the group will have the same rights, so it would be impossible to give a number of users different access rights. With NT's access control lists you can basically accomplish all the combination of user access you can think of. You can grant or deny access to as many specific users or groups as you want.

Setting the correct permissions on files  is an important part of cfengine's capabilities. When we had compiled  and tested cfengine on the NT platform we started to implement NT ACLs  in cfengine. We had to write the code ourselves. Unfortunately, creating and modifying the ACLs  proved to be more difficult than we anticipated and we were not able to complete it within the timeframe of this project. However we are well on the way and we will continue the work later. A more detailed description of access control lists and our work in that area is given in the section Access control.

One of the things that cfengine controls on UNIX systems is mounting  of file systems on the network. On NT mounting  means something different than on UNIX. All file systems are mounted  automatically at boot time as c:, d:, e: and so on. In other words you can not choose to mount a file system somewhere else in the directory hierarchy. It is a bit of a surprise that both the CygWin  and UWIN  porting layers have the mount and umount commands. We will talk about how that works and discuss if it can be used by cfengine in Chapter [*]. On UNIX systems the file systems to be mounted  and where they are to be mounted  are given in a text file often called fstab  (at least on GNU/Linux). Since NT does not have such a file we have defined its location as N/A (not available) in the file src/classes.c.

One of the things that is specified in the file src/classes.c is the location of all mail. As far as we know, there is no such thing as a default mail directory on NT the same way it is on UNIX. Therefore we cannot specify a default directory in the /src/classes.c file. However, this would in reality be a completely useless piece of information anyway, because all mail on NT is stored in a completely different format than on UNIX. Hence, the default directory is defined as N/A (not available) in the file. The mail-problem could possibly be resolved, but this is far from our biggest priority, and will be put aside indefinitely.

The location of resolve.conf, which contains network configuration on UNIX, is N/A for now. We might try to implement this for NT later on if needed and time allows us.

Compilation

To compile  cfengine on a UNIX system, one only needs to follow the instructions in README.install. The instructions say to write configure  and make .

Configure  is a shell script and it runs on both porting layers. With CygWin  we had to make a link from /bin/sh to where CygWin  had placed the sh binary. The make  program is also supported by both CygWin  and UWIN .

  
Running the configure script

Originally we did not know exactly what configure  did, but after a quick investigation we found that it checks the system and sets some variables according to which flavour of UNIX the system is running. It then creates one or more makefiles that correctly will set up and compile  cfengine on the system. However, since all NT systems are alike, we could just write our own standard makefile. This would enable the user to only run this one file to install all of cfengine on the NT system. However, we were able to run configure  successfully on CygWin  by making a few simple changes in the configure.in file and a few of cfengine's header files . When we tried to run configure  on UWIN  it was not able to find the source files, even though we told it where they were with a command line option. Since CygWin  seemed to be working better than UWIN  we decided to try to compile  with it first[+].

  
Running make

The first time we ran make  it compiled  a few files before it aborted with errors. We expected errors, but were quite excited that some files had actually been compiled . The first error was that it could not find two files.
 
    In file included from cfengine.c:37: 
    cf.defs.h:196: sys/protosw.h: No such file or directory 
    cf.defs.h:198: net/route.h: No such file or directory 
    make[1]: *** [cfengine.o] Error 1 
    make: *** [all] Error 2
We got Mark to look at it and it was easy for him to put an extra test condition in the code to make it not look for these files on an NT system. We ran make  again and got a bit further before a new error occurred. The error was as follows:
 
    ifconf.c:456: parse error 
    make[1]: *** [ifconf.o] Error 1 
    make: *** [all] Error 2
This was only a typo in the code, elif instead of else if. The next error was:
 
    ifconf.c: In function `SetDefaultRoute': 
    ifconf.c:362: storage size of `route' isn't known 
    make[1]: *** [ifconf.o] Error 1 
    make: *** [all] Error 2
We had to ignore these calls since they are not implemented for NT. We did this by commenting out the relevant code.

Timezone name

The error that caused the most work was the timezone problem. The initial problem was that the method used in the cfengine code to extract the timezone from the machine did not compile . We started to look for other functions or variables in the c header files that could give us information about the timezone. We also checked the CygWin  documentation on Cygnus' homepage and their mailing list to find a solution. All we discovered was that others had experienced the same problem. The only way to get something close to what we needed was to use the function timezone(). This function returns the timezone in the format GMT+1. Cfengine however, expected just a three-letter abbreviation like CET or MET. Since it seemed impossible to get the timezone on this format we agreed with Mark that he would edit the cfengine code, so that it would be able to handle the format GMT+/-X as well. If nothing else, the new version compiled  successfully.

Testing cfengine

After compiling cfengine we needed to test it's functionality. Even though it compiled  it might not work the way it was supposed to. We agreed with Mark to test the following in this order: shellcommands , tidy , disable , links , copy , editfiles , files  and processes . When we first ran a small cfengine program, we got an error which said that we did not have a variable HOME that pointed to the home directory. We created the variable in the bash shell with the command:
 
    export HOME=/testdir
We decided to make several small programs that just tested one type of functionality. The test programs are quoted in the following sections along with the results of the testing.

  
Shellcommands

Once we figured out the actual syntax of a cfengine program we made cfengine execute the echo and date commands. As expected this worked right away. The test program looked like this:
control:
        actionsequence = ( shellcommands )

shellcommands:

        "/bin/echo Ohh yeahhh"
        "/bin/date"

  
Tidy

The tidy  command has several options and we wanted to test all of them. However we postponed the testing of options that had to do with links  because they are handled differently in NT and the CygWin  porting layer. The following options worked as they were supposed to: pattern, recurse, age, size, type and rmdirs. Another thing we did not test was the define option. We figured that it would work because it is handled internally in cfengine. This was the test program we used:
 
control:
        actionsequence = ( tidy )

tidy:
        /testdir  pattern=*.html  r=1  age=2 type=mtime  size=10

  
Disable

The disable  command functioned perfectly. Here too, we did not check type=link/links  and define. The test program looked like this:
 
control:
        actionsequence = ( disable )

disable:
        /testdir/default.htm
        /testdir/links.html     size=>2000

  
Links

We were not quite sure how linking would work because the CygWin  porting layer simulates symbolic links  in a way that is not understood by Windows NT. A link under CygWin  is just a small text file, which looks something like this:
 
    !<symlink>destinationfile
On NT you just have shortcuts, which are binary files with extension .lnk. If we can find the format of these binary files it should be possible to make cfengine understand or at least make shortcuts. When testing we found out that (not unexpectedly) the links  command worked the way it was supposed to with CygWin  type links . These options have been checked and work properly: single links , multiple links , include, exclude, recurse, type=absolute/relative and type=hard (makes a copy of the file instead of a link). There were some options that we did not test. They were: copy , copytype and nofile/deadlinks. Again these refer to cfengine internals so they are not affected by NT.

This test program covers some of the options we tested for links :

 
control:
        actionsequence = ( links )

links:
        /testdir/texinfo.tex -> /testdir/jiffy/texinfo.tex
        /testdir/ +> /testdir/jiffy/jumbo       include=*.c
        /testdir/regexlink -> /testdir/jiffy/c/jumbo/regex.c type=hard

  
Copy

Copying  locally worked fine, but there were some problems related to file permissions. We were not able to set the file permissions as we wanted. For example only .exe files  could be (actually they had to be) executable. Changing owner and group did not work either. This is clearly something we need to devote some time to.

When we tried to copy  from another server, a UNIX server (nexus), we got some error messages. We did not understand what was wrong so we got Mark to look at it. It complained that cfengine was not registered in /etc/services, which is a file that does not exist on NT. Mark changed the cfengine code so that it would use a default port in the absence of the /etc/services file. We still got an error message, which is quoted below:

Checking copy from nexus:/iu/nexus/u1/kjenslj/downs/paper.tex to
/testdir/paper.tex cfengine:RICON: Remember to register cfengine in
/etc/services: cfengine 5308/tcp cfengine:RICON: getservbynameSystem
name is RICON.iu.hioslo.no and DNS name on this socket is
ricon.iu.hioslo.no [main]
C:\Port\CygWin\cfengine-1.5.0-alpha7\src\cfengine.exe 8063 (0)
handle_exceptions: Exception: STATUS_ACCESS_VIOLATION [main] cfengine
8063 (0) handle_exceptions: Dumping stack trace to cfengine.exe.core
It was an error message that did not tell us much. After some testing Mark tried to disable person authentication in the cfd (cfengine daemon) running on nexus. This solved our problem and we were able to copy  from an external source directory.

Another smaller problem was that the function that cfengine used to get the username did not work. While testing it we just sent an arbitrary string as the username when opening a socket (this is done in proto.c). Later Mark found another function, getlogin(), that returned the username. This is updated in the new version cfengine1.5-alpha8. However the true solution to the problem was discovered a bit later. As it turned out, the creation of /etc/passwd and /etc/group solved the problem. In other words the original code worked fine and the modifications were removed. A thought for the future would be to try to compile  the GNU authentication program that the cfd daemon tries to start when person authentication is enabled.

Finally, we did not get the purge option to work. This was probably because we did not get the syntax right. It should work since this option only affects cfengine internals.

The test file that caused us some problems when we tried to copy  from another server is listed below.

control:
        domain = ( iu.hioslo.no )
        actionsequence = ( copy )

copy:
        /etc/passwd dest=/testdir/passwd server=nexus
        /testdir/dummy/pub dest=/testdir/ recurse=1 exclude=*.htm

  
Editfiles

Mark had made a small cfengine program to test the editfiles  command. He especially wanted to check if regular expressions  worked. The results of the test program are discussed in the section below.

Regular expressions

Regular expressions  are used by cfengine to match patterns in text. A regular expression  is a way of describing a set of strings without having to list all the strings in your set. They are usually used to locate patterns within a string and replace them with something else.

When we ran the test program quoted below it was clear that regular expressions  did not work at all. We had a suspicion about this because cfengine's configure  script had not been able to locate the rxposix.h  or regexp.h  header files. It must have found something since we were able to compile  cfengine, but wherever the functions it used were located they did not do anything.

control:
 actionsequence = ( editfiles )

 # Test editfiles and regular expressions

 var1 = ( "Insert this" )

editfiles:
 { $(HOME)/cfengine-textfile

 AutoCreate
 DeleteLinesMatching "This.*"
 AppendIfNoSuchLine "You can make cfengine work like @m4 macros@"
 ReplaceAll "@m4 macros@" With "a dream"
 BeginGroupIfNoLineContaining "excite"
   AppendIfNoSuchLine "Sticks and stones may break my bones"
   AppendIfNoSuchLine "But words will never hurt me"
 EndGroup
 ReplaceAll "words.*" With "whips and chains excite me"
 PrependIfNoSuchLine "$(var1) at the start"
 AppendIfNoSuchLine "This is the end of a non-convergent file"
 }

Mark suggested that we try to download and compile  GNU's regex library rx1.5 . We downloaded it, but when we ran its configure  script we got some error messages. However it did create the makefiles so we tried to run make . Make  did not seem to do anything, so we started to look at the messages from configure . One of them was that it did not find the tsort program, which does not come with CygWin . It did come with UWIN  so we tried to copy it from there. It worked, we got rid of some errors, but make  still did nothing. The library was supposed to be installed in subdirectories of /usr/local, but /usr/local did not exist on our machine. First we thought that it would make the directories if they did not exist, but when we got more desperate we decided to create them ourselves. This was a smart thing to do, because now it actually installed the rxposix.h header file in /usr/local/include/ and librx.a in /usr/local/lib. OK, so it worked, but it would be much better if we could do it without UWIN's  tsort program. We tried to do it all again, but this time we replaced tsort.exe with a link to cat.exe. This worked just as well. We now built cfengine again and regular expressions  worked. (Note: we had to run make  distclean for configure  to search for the header files again).

  
Files

As we have mentioned earlier there was a problem when trying to set the file  permissions. It did not seem to be any connection between the UNIX style permissions seen in CygWin's shell and the permissions in NT. It seems like CygWin has made their own system. Another possible problem with files was the filenames or paths. In NT they are on the form c:\directory\file while CygWin sees it as /directory/file. However it seems like CygWin handles this for us. They also provide the utility cygpath that converts native filenames to CygWin UNIX style pathnames and back.

File permissions

When consulting the CygWin  documentation we quickly discovered that CygWin  has two ways of dealing with file permissions. Under Windows 9x files are always readable and CygWin  checks the native read-only mode to determine if they are writable. Files that have a name ending with .exe, .bat, .com or content that begins with #! are executable. It follows that the chmod (change owner) command only affects the write mode. This way of dealing with the file permissions is default on NT as well. However, on NT, you have the opportunity to make the file system behave more like Unix systems by setting the CYGWIN  environment variable to ntea (NT Extended Attributes). According to the CygWin  documentation this works well on NTFS  partitions, but not so well with FAT  partitions. We set the CYGWIN variable to ntea in /Cygnus/cygwin-b20/cygnus.bat. We were now able to set the permissions as we wanted. Still, we can't quite figure out the connection between traditional UNIX style group/other permissions and NT. The group/other permissions do not have any effect on the ACLs  (Access Control Lists). The ACLs  on the other hand can be changed with the cacls command, and it actually works in both NT and CygWin .

Owner and group

At first the chown command did not seem to work. The user always had user id (uid) 500. We thought that this was a bit strange so we confronted the CygWin  documentation again and found the solution there. CygWin  provides the mkpasswd  and mkgroup  utilities which generates the files /etc/passwd and /etc/group respectively. These utilities check NT's user and group information and print it in UNIX format to stdout. The reason why they added these utilities was that many UNIX programs expect to find the passwd and group files. Among them the chown command used by cfengine. After generating these files the chown command worked as it was supposed to. To set this up you would write the following in the shell:
 
$ mkdir /etc 
$ mkpasswd > /etc/passwd 
$ mkgroup > /etc/group
This information is static and the files need to be regenerated if you change the password or group information on your system. It could be a good idea to have cfengine run these programs.

We were now able to run this test program without any problems as well:

control:
        actionsequence = ( files )

files:
        /testdir/dum_fil.txt    owner=kjenslj   group=Everyone  action=touch

Access control

An important function of cfengine is the security issue and file permissions in particular. By controlling the access rights of certain files the security of a system is greatly improved. However, setting the rights on files  differs from the various UNIX systems to Microsoft's  Windows NT. CygWin  has done something to simulate UNIX rights on NT, but this is far from satisfactory. To make it possible for the users of cfengine to take complete advantage of the complexity of NT's access control system we needed to produce additional code that would manipulate the low-level structures of NT's operating system.

  
Security descriptors - SDs

To handle permissions not only on files  and directories but also on any other securable object such as threads, pipes, processes  and their likes, NT assigns an SD  to hold the security information about the object. This information includes two Security Identifiers (SIDs) that effectively identifies the owner and the primary group of the object. The SD  can also contain Access Control Lists (ACLs  ) which are lists of user accounts and groups and their associated permissions. When NT attempts to decide whether to allow access to a securable object, it examines the security information contained in the object's security descriptor . Most importantly it examines the list of access control entries contained in the ACL .

  
Access control lists - ACLs

The ACL  is a structure, which among other things holds information about the total size of the ACL , the number of ACEs  that are linked together and the first ACE  of the linked list. A closer description of the ACEs  is given in Section [*]. As of today there are two types of ACLs ; discretionary  and system .

   
Discretionary ACL - DACL

A discretionary ACL  determines which users and groups can and cannot access the object, and it is controlled by the owner of the object or anyone that has been granted the right to change the permissions of the object. When identifying the amount of access a user should be given to the object, the system must first iterate through the DACL's  list of ACEs  until the user in question is found. In the case that no DACL  is provided with the object's security descriptor , full access will be granted. On realising this fact it became clear that it was the manipulation of the DACLs  that would be our main goal when writing code that would enable cfengine to set file permissions on NT.

   
System ACL - SACL

This type of ACL  can allow the system administrator to log any attempts to gain access to an object. The SACL  can have a list of ACEs  just like the DACL , but these will determine which users and groups that will be audited. However, we decided to omit implementing code that would enable manipulation of SACLs . Time will not permit us to implement all the things that we would like to, and auditing is not our major concern. We believe that the number of objects that a system administrator would like to audit is limited to a few, and so we concluded that for the limited use of setting the auditing information on objects, the graphical user interface of Windows NT should be adequate. For now.

    
Access control entries - ACEs

The ACE  contains information about the access right of a specific user or group identified by a SID. There are several types of ACEs , but as we have chosen to focus on file permissions, we have worked with only two types of ACEs ; Access Allowed ACEs  and Access Denied ACEs . As the names insinuate, the Access Allowed ACE  grants the permissions specified in the ACE  to the user identified by the ACE'sACE!SID in SID, while the Access Denied ACE  denies the permissions. Every user or group that is to be granted or denied access to a file, directory or any other securable object has an ACE in the object's ACL  list of ACEs . However, it is important to note that conflicts may occur. A certain user may have an ACE  where he is granted access to the object, while at the same he exists in a group that also has an ACE, only this ACE  denies access. Shall the user be granted or denied access? As we are about to explain - and there is of course no doubt in your mind - NT does in fact deal with this problem.

  
The order of the ACEs

When programming at one of the lowest levels the system will permit, it is immensely important to fully understand how the system operates. A misunderstanding can turn into a grave security issue, and it can easily lead to consequences of proportions yet to be thought of[+]. Needless to say we have made an effort in understanding NT and how it deals with files, accounts and permissions among other things.

Granting access

When a user logs on to NT he is given a security token. The token corresponds to the users ID as well as to any group the user may belong. For a user to be granted access to an object, two conditions must exist. Firstly, an ACE  granting the permissions that the user's token is asking for must exist in the ACL , and secondly there must be no ACE  in the ACL  that specifically denies the user access to the object. If the end of the list is reached and no ACE  was able to provide any of the two conditions above, any user is implicitly denied access.

Deciding access

An ACL  may or may not contain a list of ACEs . If no list exists, any user will automatically be denied access to the object, otherwise the system will iterate through the list of ACEs  to determine whether or not to grant access to an account. NT's security manager will stop at the first ACE , which either grants or denies access to the account in question. See figure [*]. This makes the order of the ACEs  a crucial factor to whether a user is granted or denied access. If the system finds an ACE  where a user is granted the permissions he asks for, the system will stop searching and grant him the access even if the next ACE  denies him the permissions asked for. To ensure that the correct permission is granted, NT orders the ACEs  in a particular order, enabling a very complex system indeed[+]. The ACEs  within an ACL  must be ordered correctly and by category as follows: First all the non-inherited ACEs , then the inherited ACEs . The ACE's in each of these two groups must be ordered as follows: Access Denied ACEs , Access Denied Object ACEs , Access Allowed ACEs , Access Allowed Object ACEs  that refers firstly to object GUID, secondly to property set GUID and finally to property GUID. The ACEs  in the group of inherited ACEs  should be divided into new groups, one for each generation of inheritance. This is to ensure that ACEs  inherited from a parent container have precedence over ACEs  inherited from a grandparent container. As we were manipulating the ACLs , we soon realised and must regretfully admit that we found the ordering hard to understand and difficult to implement. This seems, however, to somewhat contradict the Microsoft  Corporation as they in their developers network boldly claim: "This ordering provides intuitive access validation semantics and optimal access validation performances".
   Figure: Order of ACEs in a DACL

[IMAGE png]

  
Implementing ACLs in cfengine

Before starting to write the actual code it was important to have a plan for how it should be done. First of all we did a bit of research to try to understand how the system handled access control lists. Our research is described further in the next section. When we had the necessary background information we sat down and started thinking about what needed to be done. From experience we know that something you haven't thought about is likely to turn up later in the process. We therefor tried our best to think of all possible problems. As it turned out we had overlooked a few minor things and one more serious thing. Anyway, we came up with the following plan. The first step in implementing the ACLs  was to make sure we had the information we needed to create an ACL , i.e. to get the information from the configuration file. We also needed to convert the permission mode given in the configuration file to the access masks used by NT. We would then start by implementing the overwrite functionality with access allowed ACEs . Our research told us that this would be the easiest to code. Then we would add the code needed to append ACEs  to the ACL  and make all of this work with all functionality. Finally we would code the creation of access denied ACEs, which should be almost the same as for allowed ACEs .

We put in a lot of time and effort to make cfengine capable of creating and editing ACLs   on the Windows NT platform. Unfortunately it proved to be more difficult than we had anticipated and we were not able to finish the work within the time limit of this project. But we now have a very good understanding of ACLs  on NT, so we will finish our work at a later time.

Research

Our research was based on a book about system programming on the Win32 platform, a book about user administration on Windows NT and the cfengine code related to ACLs  on Solaris [+]. We read about system security and access control lists on NT. When we felt that we had a good understanding about how ACLs  worked on NT we tried to get a picture of how cfengine dealt with ACLs  on Solaris . We did this to be able to integrate our code in the best way possible. Before we started coding in cfengine we decided to make a couple of test programs to check that we could change a file's ACL . We made two test programs that were based on sample code from the book first mentioned. One of the things that was hard to understand was how the access mask corresponded to the rwxdpo permissions you see in NT's user interface. None of the books said anything about this, all we could find was a listing of constants (bit masks) that could be ORed together to make up the access mask. So we decided to use one of the test programs to see which permissions these constants would create. By using combinations of these constants we were able to make masks that gave the rwxdpo permissions and all combinations of them. We later discovered that this could be accomplished with other masks as well and that the ones we had used were not sufficient for our use in cfengine. This problem is described in Section [*].

Preparations

Before we started coding the actual creation of ACLs  we needed to find out how cfengine parses its config file and presents the information to its ACL  subsystem. We would also have to find a way to specify in the config file if the ACE  was to be of type access allowed or access denied. This extra information would also have to be passed on to the ACL  subsystem. Most of our work will be in the subsystem though. One problem is converting the permissions given as a string in the config file to NT's 32 bit access mask(bit sequence). These topics are discussed in the following subsections.

The config file

Cfengine already supported ACLs  on Solaris  and DFS[+] so there existed a way of defining an ACL  in the configuration file. Of course we could have made a totally new way of defining an NT ACE , but we found that to be unnecessary. The information we needed to make an ACE  on NT was the same as on Solaris  and DFS. We only needed one additional piece of information; if it is an allowed or denied ACE . We also wanted to make it as easy as possible for the users of cfengine by making it similar to what already existed. We thought of two possibilities to add this information to the ACE  definition in the configuration file. The first idea was to write it like this:
 
method:overwrite 
fstype:nt 
access:allowed 
user:gustafb:rx 
user:kjenslj:rwx
We started to look in the cfengine code to analyse how difficult it would be to implement this solution. As we got into the code we understood that there was a better way to do it. A solution that meant less change in the existing code. The format we decided on is as follows:
 
method:overwrite 
fstype:nt 
user:gustafb:rx:allowed 
user:kjenslj:rwx:denied
This was rather easy to implement but to make it work we made some changes in the CFACE  struct (see below). For Solaris  and DFS you can specify the access mode as r, w, x, all, default and noaccess[+]. In addition to this you can use these modes on NT: d, p, o, read and change.

  
Using the CFACE struct

The CFACE  struct is a struct that was used to represent the Solaris  and DFS ACEs  in cfengine. Here too we had the choice between making our own representation and trying to use the existing. We decided to use the CFACE  struct because it was clear to us that it would mean less additional code. It would be much easier because we could use the existing code to put the information from the configuration file into the struct. To use it we had to add an extra variable to hold the access type (allowed/denied). This variable is only added if the code is compiled  on a Windows NT system.

Parsing the config file

Due to the fact that we chose to use the CFACE  struct to represent the ACEs  and the way we defined the ACEs  in the configuration we did not have to add much code to parse the config file. All we had to do was to make sure that the access type was put into our new variable in the CFACE  struct.

From mode to NT's access masks

In the configuration file the permissions are given in the form rwxd or a keyword like change or all, i.e. a string that we will refer to as mode. When creating an ACE  we need a 32 bit access mask (When we use mask in the text we are referring to this access mask). So we have to perform a conversion. We created the function getNTModeMask that receives the mode and a possible old access mask as parameters. The way this works is that the mode is converted and ORed into the old mask, which can be empty. The old mode is converted by first checking the keywords, if it is a keyword the corresponding mask is ORed into the old mask. If it is not a keyword we know that the mode is in the format rwxdpo. If so we iterate through the mode and for each letter the corresponding mode is ORed into the old mask. Finally the function returns the old mask which now has become the new mask.

   
Creating ACLs with Access Allowed ACEs

There are two methods of setting file permissions through cfengine's config file. One way is to use the append method which will not remove any permissions on the existing ACL [+], but as the name of the method insinuates only add the permissions specified. The other way is to overwrite the existing ACL . This latter method will in effect exchange the existing ACL  with a new one containing the permissions specified in the config file. After having successfully parsed the config file, every user that is to be allowed or denied access is stored in a list of CFACE  structs with the wanted mode represented as a string. We have already coded the function getNTModeMask to return an NT access mask on receiving a valid UNIX-like mode as a string. The list of CFACEs  now possesses all the relevant information about the file permissions and is only waiting to be used in a productive manner.

  
Overwriting an existing ACL

We decided to wait with the appending until we had managed the overwrite functionality. Based on one of our earlier test programs we knew that to create a file with a new ACL  was as simple as stealing candy from a baby. However we did not want to create a file with an ACL , we wanted to overwrite an existing file's ACL . This is slightly more complicated, but even stealing candy from an adult is rarely very hard. What we needed to do was to take every user in the list of CFACEs  and use one of Windows NT's functions[+]to look up the name and find each user's security identifier (SID). Then we simply initialised a new ACL  and added an access allowed ACE  with the correct NT access mask for each of the SIDs[+]. The new ACL  now has a list of ACEs , one for each user specified in the config file (if the user existed). Another function call gives us the existing file's security descriptor  (SD ) and by including the new ACL  in the SD  we just found we have successfully overwritten the file's ACL  and in effect replaced the file's permissions with those specified in the config file.

The logic on how to overwrite an existing file's ACL  is simple enough, but we could of course not manage this step without running into some problems. Our code was pretty much based on examples we knew to be right. However for some unknown reason our code still would not work properly. After several hours of debugging we finally found that the NT function GlobalAlloc[+] had failed to work as expected. The function is supposed to allocate memory and as of today we still do not know why it failed. We did not receive any errors or warnings though. The program simply failed to work as expected. We solved the problem by calling the C function malloc to handle the memory allocation. This was not the end of our troubles though. Suddenly a new error appeared, and again it took us several hours to find the cause of it. To make sure that we allocated the exact amount of memory for the new ACL  we had used the function LookupAccountName twice. Once to check if the user existed and then again to actually get the size of the user's SID and to allocate memory. We commented out the first call to the function and miraculously the error disappeared. We kept the program without the test knowing that in certain cases we might now allocate too much memory for the new ACL [+]. We were able to fix this a few weeks later, mind you.

   
Appending ACEs to an existing ACL

Appending ACEs  to an existing ACL  is a little bit trickier than overwriting one, but only a little. It is pretty much like the overwrite method, only after initialising a new ACL  we must not only add an ACE  for each user specified in the config file to the new ACL , but we must also copy the existing ACL's ACEs. We did this by iterating the ACL  contained within the existing file's SD , adding every ACE  we come across to our newly created ACL . Then we created a new SD  containing the new ACL , thus completing the method of appending.

There was one issue that we had omitted to consider though. We realised the effect of appending ten ACEs  belonging to the same user, let us call him john, to the same ACL  only after we attempted it. It seemed that the operating system interpreted the ten ACEs  by only showing one of john's ACEs  on the graphical interface but with all the accumulated permissions. This was the exact effect that we had hoped for, but no matter how NT interprets several ACEs  belonging to the same user, one fact still remained; we were using unnecessarily many ACEs  and really wasting disk space. Maybe not much. Maybe not more than some tenfold bytes, but wasting disk space on such a low level can quickly lead to several gigabytes of wasted space! We knew we had to correct the situation. We also realised another far more serious issue. During the planning phase we had not considered the implementation of an important feature of cfengine, namely the removal of existing rights through the use of a minus sign in the config file. We realised that this last functionality demanded some restructuring of our code and decided to enhance our code to the best of our ability making sure that no memory or disk space were lost, and that the speed and the effectiveness of the code was optimised. However, implementing the removal of rights and the new optimised structure of our code turned out to be a little more time consuming than we first anticipated.

Extended functionality

To make sure no ACEs  of the same type for the same user is added to the ACL , and to implement the removal of rights as well as to ensure as quick and effective code as possible, we spent almost a day thinking and planning the restructuring of our code. We decided to extend the functionality of our function getNTModeMask so that by sending the old access mask in addition to the wanted mode as parameters, the function would return the new modified access mask[+]. With this functionality, changing an existing access mask should be a simple procedure. But in addition to removing rights of existing users we also needed to avoid the needless adding of several ACEs  for the same user.

Optimisation and subtraction of rights

The only time when subtracting rights is of interest, is when using the append method to fix a file's permissions. We have already explained how we used to append ACEs  to an existing ACL . However, our way of going about it had to change in order to fix possible waste of space. To keep as much of the existing code as possible we decided to continue using the CFACE  struct to hold the necessary information, only now the struct would also hold NT's access mask in addition to the string representing the wanted permissions. On parsing the config file and creating the list of CFACEs , the mask in the struct is initialised to zero. When writing mask we are referring to an unsigned integer holding the permissions, while when writing mode we are referring to a string representing permissions as one is used to on UNIX. The addition of the mask member was added to enable the removal of rights. If we had omitted adding this new struct member, changing existing rights would be unnecessary difficult. We would have to analyse NT's access mask and transform the mask to a string representing the permissions. Then we would need to compare this string to the string with the wanted permissions, using the result of this comparison to decide what the new mask should be. Now instead we only send the old mask and the new mode to our function getNTModeMask and storing the result in the mask member of the struct. The function is designed so that it will return the desired mask whether the existing mask sent to the function is 0 or if it contains a value.

The new shape of the CFACE  struct and the added functionality of getNTModeMask solved the removal of rights. However the problem of possibly creating too many ACEs  still remained. To start checking the new ACEs  against every existing ACE  would be a fairly heavy and time-consuming operation. Besides, because of the way NT interprets two ACEs  for the same user, in order to alter the rights of an existing ACE  we were forced to remove the ACE  from the ACL  completely before adding it again with different permission bits set. This implies a far better solution. We still have to check every user against every existing user before creating an ACE  for the new user, but we do it before any ACEs  are created at all. We do it while the users are still in our list. To implement this we had to create a new list of CFACEs . According to our plan, this new list would eventually end up with a struct for every single user that is supposed to have an ACE  in the ACL . Each of the users in the list should also have the correct mask set. To guarantee that no user that should be in the new list was left out, we had to iterate the old and the new list several times, but there is really no way around it. Well, actually there is through some fancy pointer manipulation, but as cfengine is a delicate program and the winnings of the manipulations are so slight as to be almost imperceptible, we decided not to go through with it. We did not think it would be worth the time either as we have plenty of things to do.

The way our code works now is that we first of all extract all the ACEs  from the existing ACL . Each of these ACEs  are checked with our old list of CFACE  structs containing the users specified in the config file. If there is a match on the usernames as well as on the access type, a new mask is calculated by the function getNTModeMask. The user with the correct mask is then added to our new list of CFACEs . The users mask in the old list is also set. Since the mask originally is initialised to be 0, we will know which of the users in our old list of CFACEs  that did not already exist in the ACL . These will be the users that still have a mask of 0. So when all the existing ACEs  are transformed into CFACEs  and inserted into our new list, all we need to do is to iterate through our old list one more time. This time we take all users with 0 as mask, find the mask corresponding to the mode, and then add them to our new list. We now have a new list of CFACEs  with every user occurring only once with the same access type, and with possibly modified but certainly correct access masks. The last step is to iterate through our new list twice. The first time looking for all the structs that have access type denied, creating an Access Denied ACE  for each one and adding the ACE  to a new ACL . The second time we create an Access Allowed ACE  for every struct that have access type allowed and add the ACE  to the ACL . We add all the denied ACEs  first because as we have mentioned before, the order of the ACEs  is of utmost importance. When inserting the new ACL  to the file's security descriptor  we have successfully modified the file's permissions. We later discovered, however, that we had overlooked an important factor when updating a file's ACL . We had omitted setting each ACE's  inheritance flag, but this is described in more detail in the section on directories.

   
NT's access mask

There are six different types of permission on a securable object in NT. We presume you are already familiar with UNIX's three rights read, write and execute. However, NT has defined some other rights as well. These are delete, permission and owner. The delete permission enables the user to delete the file, permission rights enables the user to change the permissions on the file, while owner permission enables the user to take ownership of the file. Note the importance of being careful with granting permission rights to a user on a file as he can then remove everybody else's rights on the file, including all administrators and the operating system. We will from now on usually refer to the different rights with a character for each right, where r equals read, w equals write, x equals execute, d is delete, o is for owner and p represents permission.

The way NT specifies the permissions on a file is through setting different bits in a mask that is a variable of type DWORD. The DWORD is 32 bits long and can compare to C's unsigned long int. The 32 bits are divided into groups where, counting from the right, the first 16 bits are for specific rights, the next eight bits are for standard rights, while the last and leftmost eight bits are for generic rights[+]. For reasons far beyond our capabilities of reasoning, NT has chosen not to go with the intuitive solution of simply reserving one simple bit for each possible right. Instead one must set several bits to specify a certain right. For example, to specify read rights, a full five bits must be set. Six bits must be set to specify write, and four on execute. To further complicate matters, some of these bit masks overlap. So if the bits are set for read and execute rights and you want to remove the read right, you cannot simply unset all the read bits, because some of them are used by the execute right. We have not yet understood why NT has chosen to do it this way and we are still waiting to be enlightened. It is perhaps to use the mask on other securable objects than just files, but the fact that it complicates things a bit still remains.

To make things easier for someone in our position, someone who is attempting to set certain rights by using their own program, NT has defined some constants. They are in fact bit masks. Some of these are GENERIC_ALL, GENERIC_EXECUTE, GENERIC_READ and GENERIC_WRITE. These generic rights constants are pre-defined combinations of standard and specific rights and change from object to object. By experimenting in our test programs we found which ones would set r, w, x, d, p and o respectively. However, even if using these constants had the desired effect, things quickly turned more complicated when we needed to subtract rights. Removing rights would obviously require bitwise manipulation of the mask, and this is not a problem in itself. To remove, say the read permission, we would simply AND the old mask with the complement of the constant representing the right. In pseudo code, something like this should do the trick:

   OldMask &= ~GENERIC\_READ
The result would be the old mask with r removed. However, since NT operates with bitmasks that overlap each other, removing the bitmask for r would also remove part of the bitmask used by w and x (if one of those permissions were set). Unintentionally we are sure, but nevertheless a misfortune that the documentation of the access mask and exactly which bits are used for what has failed to be included in every single piece of documentation that we have checked. Through the method that has become oh so very familiar to us lately, the method of experimenting and trying and failing, we managed to map the bits used by the different rights. See Figure [*]. With this new knowledge we could manipulate the mask without using the constants and could successfully remove any permission we wanted. For example, a file with permission rx will according to our map have the bits 20,17,7,5,3 and 0 set, whereof bits 20,17 and 7 are shared. To remove the r permission we can only remove the bits that are used exclusively by this permission, i.e. only bits 3 and 0. Bit 3 and 0 in a 4 bit sequence can be represented hexadecimally as the number 9. The complement of this number is the hexadecimal number 6. Since we must work with the complement of the permission to remove that permission, we would now do something like this to remove the r from rx:
   OldMask &= 0xFFFFFFF6
Each of the F's will not change the existing bits they are ANDed with because the F represent a four bit sequence where every bit is set[+].

The problem with removing specific rights now seem to be solved. There was only one thing that needed correction. NT has some predefined permission types. These are No Access, Read(rx), Change(rwxd) and All(rwxdpo). When giving a user the permissions rx or rwxd NT automatically interprets this as Read and Change respectively. However, when setting all the permissions (rwxdpo), NT does not interpret this as All even if that is the effective right. As we could find no explanation for this anywhere in the documentation that were available to us, we had to experiment again. We discovered that for a user to be given the predefined permission All, the GENERIC_ALL bit must be set, i.e. bit 28. Before returning the mask from the function getNTModeMask we check if all the bits for rwxdpo respectively are set, and if so we return a mask with only bit 28 set instead.

Setting No Access was harder and it took us a long time until we figured out how to do it. Again no documentation on the subject seemed to be available. Experimentation told us that the mask for No Access is the exact same mask as for All. This made no sense to us as these two rights are the complete opposite. We were on the verge of mailing Microsoft  for a hint because this made no sense. It was only by chance that we later discovered how it worked. The documentation claims that the graphical user interface of NT 4.0 is unable to show Access Denied ACEs . We therefore assumed that all the ACEs  showed when looking at a file's permissions were Access Allowed ACEs . However we found that there was an exception. No Access is really an Access Denied ACE  with All rights, i.e. with a mask where bit 28 is set! We were utterly surprised to learn this fact, but we were also relieved. By the time we figured this out the problem had really caused a lot of frustration.

When working with a securable object we believe that it is the specific rights that specify a user's permissions on the object (except when All or No Access is given). This have not been extensively tested but seem to be the case on files, and files are about the only securable object we work with. An exception are directories where things seem to work a little different, but this is discussed further in a later chapter. Nevertheless, with a complete mapping of the bits in NT's access mask and a full understanding of how the pre-defined rights affect the mask, we hope that our newly acquired knowledge can be of someone else's assistance as well.
   Figure: NT bit masks for the different permissions.

[IMAGE png]

Default

One of the keywords that can be used for the access mode in the configuration file is default. To use it you would write something like:
user:kjenslj:default:allowed
The use of the default keyword should result in the removal of the respective user's ACE  from the ACL . When using default the access type does not have to be specified, so allowed could have been omitted from the example above. The result would have been the same. It was not difficult to remove the ACEs  belonging to users or groups when the mode was set to default. In the function getNTModeMask we return 0 if the mode is default. If the method is append we check the ACEs  that exist in the old ACL  against the list of CFACEs  from the configuration file. Then we add a CFACE  to a new list, unless the mode is 0 like it will be if the mode is default. Finally we add all the ACE s that are in the configuration file and not in the old ACL . The entries in the list of CFACEs  from the configuration file with NTMode equal to 0, i.e. the NTMode has not been updated. There is one exception though. If the mode is default the NTMode have been updated, but it is still 0. Therefore we also test that the mode is different from default before we add them. In case the method is overwrite we just check that the mode is different from default before we call AddAccessDeniedAce or AddAccessAllowedAce.

Noaccess

Noaccess is another keyword that can be used in the configuration file. The effect of this on the UNIX platform is that all rights are removed from the user or group in question. It should work the same way on the NT platform as well. Windows NT uses the keywords No access in its user interface. When NT uses the No access the ACE  is an access denied ACE  with all rights, i.e. the user or group is denied all rights. Removing all rights on UNIX and denying all rights on NT have exactly the same effect. So when noaccess is specified in the configuration file we create an access denied ACE  with all rights. To accomplish this we made some changes in the code were we put the information from the configuration file into our CFACE  structs. Before we put the mode into the struct we check if its value is noaccess. If it is we set the mode in the struct to all and the access type to denied. In other words it has no effect to specify the access type in the configuration file when you use noaccess. We decided to do it this way because then we did not have to change any of the code we had written already. Of course we could have had a test later in the program. For example we could have tested it in the function getNTModeMask were we test for the default keyword. The reason why we did not was that we cannot change the access type in this function because we don't have access to the CFACE . So if we were going to check it there we would have to change the function and why would we change anything that works so well.

   
Creating ACLs with denied ACEs

We had saved the access denied ACEs  for last. They are created in exactly the same way as the access allowed ACEs . The only difference is that we call the system function AddAccessDeniedAce instead of the AddAccessAllowedAce. In other words it should not be too difficult to add this to the code that added allowed ACEs . The problem with making denied ACEs  is the fact that they cannot be viewed or changed from NT 4.0's graphical user interface. A bit strange maybe, that they have implemented it but not given the user the possibility to use it. Unless they use a program that can set them, like cfengine. Microsoft  says that from NT 5.0 (Windows 2000?) it will be possible to view and change access denied ACEs  from the user interface. There is however one access denied ACE  that you can view and that is a denied ACE  with all rights, which appear as No access in NT's user interface. That makes it seem even stranger, you can view one but not all. You should be able to use the keyword noaccess in the configuration file as well. When the information from the configuration file is read into the CFACE  structs we change the mode from noaccess to all and set the access type to denied.

We ran into a problem when we made access denied ACEs  with all rights, i.e. with noaccess. Sometimes we were able to view the ACEs  in NT's graphical user interface, but sometimes not. We suspected that it might have something to do with the order of the ACEs . We consulted the documentation on Microsoft's  web site and found this to be true. All the access denied ACEs  should be placed ahead of all the allowed ACEs  in the ACL .

When implementing the denied ACEs  a bug that caused the program to not add some ACEs  occurred. For a long time we thought that this had to do with the noaccess functionality that we had just implemented. However the bug seemed to occur randomly so we suspected it could have something to do with memory. We finally found the bug. We did not allocate enough memory for the new ACL . The function we made to calculate the size only gave the size of all the ACEs  and not the ACL  itself. We added the size of an empty ACL , which was 8 bytes, and then everything worked fine. Finally we thought we had finished the implementation, but as we describe in the next section there was one thing we had overlooked.

Directories

Just when we thought that we had finished the work with ACLs  we discovered something that would mean that we would not be able to finish it before this report was due. From the beginning we had assumed that files and directories were the same. That is the way it is on UNIX platforms. But hey, everything is files on UNIX. You could say that we should have checked this out earlier, but when you think you know something it does not occur to you to check if it is correct. Besides it is not mentioned anywhere in the documentation or the books we have read that the ACLs  differ from files to directories. After we discovered that it wasn't the same we really tried our best to find some information about the subject, but we were unable to find anything. Looks like we will need to find out how ACLs  works on directories on our own.

The way they differ from files is that every entry for a user or group in the ACL  has two sets of rights. The first set is directory access while the other is file access. The file access permissions are the permissions that will be inherited by files  created in the directory. The directory access permissions says as you might assume who will have access to the directory.

We started to test how the ACLs  worked for directories. First we had thought that the access masks used in the ACEs  could be different, but we discovered that this was not the case. We used our test program to get a hold of the ACL  of a directory and print all the ACEs . What we discovered was that every user and group had two ACEs , except for the ones where either directory access or file access weren't specified. This presented a new problem; how does NT know which ACE  is directory access and which is file access? The information in the ACEs  seemed to be the same so we started looking closer at the access masks. It looked like the masks specifying directory access used the specific and standard part of the mask, while the ones specifying file access used the generic and standard part. This led us to believe that the mask format told NT which ACE  applied to which type of access. But when we thought about it we found that if you had two ACEs  with permissions that only contained d, p or o it would not be possible to tell them apart because both masks would only have bits set in the standard part. So there had to be another way to tell them apart.

Another thing we discovered when we tried to find information about ACLs  on directories was that the ACE  header contained a flag that said something about inheritance of permissions. We did not know of this flag and therefore we had not preserved its value when we put the ACEs  that existed in a files ACL  into a new ACL . However we feel that we must blame this on the book we used because it did not say anything about this flag when describing how to add ACEs  to an ACL . It is extremely relevant information and should have been mentioned. Since we found out about the flag we believe that its value can tell the system if the ACE  should be directory or file access permissions. We have not had time to test this though so we cannot say for sure.

No matter how NT tells the two sets of permissions apart we have to find a way to define them in the config file. We thought of a few possibilities. For example you could have two entries for each user in the config file, but then it would be hard to use the ACL for both files and directories. We have come up with another solution, that we believe will solve this problem. The idea is to write it like this:

user:kjenslj:rwx/rx:allowed/allowed
The first set of permissions (rwx) would be the specific rights, i.e. the rights that apply to the object we are working on. The other set (rx) would be generic. If we are working with a file only the specific rights will be used, while the other (rx) will be discarded. If it is a directory the specific rights will define the directory access. The other set of permissions (rx) will define file access, i.e. the permissions inherited by files created in the directory. The first access type applies to the first set of permissions, and the second access type to the other set.

After all our work with ACLs  on the Windows NT platform the feeling we are left with is one of frustration because the documentation available on the subject is far from sufficient. Perhaps the documentation exists but in that case it is very hard to find. When you work with the system at such a low level it is hard enough without having to find out how everything works by trying and failing. Perhaps our experiences and our documentation will be of help to others trying to modify ACLs .

    
Mounting file systems

As we have said earlier, all file systems are mounted  automatically as c:, d: and so on by NT. Although manual mounting  doesn't exist on NT, CygWin  has provided the mount  program that works much like the POSIX command by the same name. You use this program to map a drive onto the directory tree. (Is this only visible in the CygWin shell). The program works by mapping the first parameter (a DOS path) to the path given as the second parameter. Mark has tried to make this work by calling a script instead of the fstab  file. This has not been tested and we do not know if it will work.

  
Process status - ps

As we established early on, the ps  command provided with CygWin  was not sufficient. This was because it only showed the processes  using the porting layer. If it was going to be of any use for cfengine in administering NT it would have to show all processes  on the system. So if time had allowed us we would have tried to make our own ps  command. The first step would have been to check if there already was a DOS command that gave a process table. If so we might be able to use that. Perhaps we would have to change the output format to suit cfengine with a script. Another solution would be to make a script that did the whole job. This is one thing that we will look into in the future.

We are aware that there is a pstat  command in NT Resource Kit that could work, but since it is a commercial product we would like to avoid it. However, many NT administrators might already have this kit so they could use it.

The registry

Editing the registry  was really a thing we wanted to look into if we had time at the end of the project. Unfortunately we never got that far, but we have some thoughts on the subject. One way to do it would be to make it a new functionality in cfengine, i.e. a new command. This would probably be a lot of work, and would require additional code in many of the source files. Another way to do it would be to use the editfiles  functionality. Of course the registry  is binary and with cfengine you can only edit text files. However we can use a pre-processor in editfiles . In other words run a filter on the registry . This could be a script that made editfiles  capable of editing it. Cfengine already has support for this. So we only need to make or get a hold of a script that did the necessary conversion. This is probably the solution we would try first because it would mean that we didn't have to make any changes in the cfengine code. If we find such a program it would be quite easy to test it and see if it could work in co-operation with cfengine. Just like the ps  command we would like to look into this in the future.

Security

Improving system security is a very important part of cfengine's tasks on the UNIX platform. By making sure the system is configured correctly and checking that file permissions are correct, cfengine improves the overall security. Being able to use cfengine to secure the Windowqs NT system was one of our goals with this project. If we reached this goal we think cfengine would be an appreciated tool for many NT administrators.

Thoughts on NT security

Since the thing we have worked most with on this project was setting the file permissions on NT we have really gotten a good look at how file security works on NT. Working at a low system level we have discovered the complexity of the ACLs  which are the core of access control on NT. First you think that such a system will be good because it enables you to be much more specific when you grant people access to files. And for people who really know what they are doing this might be true. However, a complex system might not mean that it is a better system. The more complex the system, the easier it is to overlook things. Take NT's ACLs  as an example. When a file can have an unlimited number of ACEs  both for groups and users it is easy to loose control over who really has access. Another thing that makes it difficult to control is that you rarely see the access rights of a file. To view or change the permissions you have to right click with the mouse on a file in NT Explorer, choose properties, then choose the security tab in the dialog box and finally click on permissions. This is a good example of how the user interface works against the user by making things harder than they should be. Just imagine having to change the access rights on a lot of files . It would take a long time and you would get pretty tired of clicking the mouse after a while. Having a program like cfengine take care of it for you would be quite a change. Instead of clicking the mouse all day, cfengine could check the file permissions every night or as often as you would like.

Cfengine's role on NT

As we described in the section above, cfengine could be very useful in checking and changing access rights on files  and directories on an NT system. Especially system files  would be important to control. Our intention was to make a configuration file that contained proper access rights for a number of system files . We recently heard about some system files  that were given the wrong access rights when NT is installed.

Another thing that cfengine can control on NT is disc space. Hard discs tend to fill up quite rapidly these days and a lot of it is because of unnecessary files. With the extensive use of the Internet you could for example have cfengine delete temporary Internet files on the system. Basically you can program cfengine to keep your hard drives tidy.

Deleting certain files  is not the only thing that cfengine can do. It can also copy  files . There are some files that need to be present on the system. Cfengine can check if they are in their proper place, if not it can copy  them from a backup. It can also make sure that home directories of the users contain the files they are supposed to.

With a new implementation of the ps  command cfengine will also be able to control the CPU usage. If a process uses too much CPU time cfengine can send a signal to the process, usually a kill signal.

Conclusions

Cfengine is a much used tool on UNIX, and a very successful one. It performs a lot of important tasks for the administrators of UNIX networks. We believe that it could be just as successful running[+] NT. Unfortunately we did not reach all our goals in this project. Still, with the problems we ran into we are pleased with what we have achieved. Of course we had hoped to at least finish the implementation of ACLs , but the foundation for cfengine on NT has been made. In fact cfengine does compile  and run on the NT platform, but its functionality is so far a bit limited. We have tested the functionality from UNIX that makes sense on NT as well and it works fine. In addition there is not much work left before the ACL  functionality is complete. With the ability to set file permission it will be an even more powerful tool.

The best thing however, might be that through our work with NT we have ourselves seen the need for a tool like cfengine. Windows NT is a very complex system which makes it difficult to have full control of everything. We have just administered one machine and we can only imagine how it would be to control a large NT network. With cfengine, NT administrators will be able to atomise a lot of the work that needs to be done. But even more important is the fact that cfengine will look after the system and reduce the chance of errors that the administrators will have to deal with. The computer system becomes less dependent of human intervention.

Acknowledgements

The authors would like to thank Mark Burgess for inspiration, believing in us and giving us the chance to be a part of this project. Tore M. Jonassen for being helpful with any questions we may have had. Jørgen Botnan and Håvard J. Hauge for listening to our complaints when things were not working out the way they were supposed to, and for lending us their book on C programming every now and then.

Trademarks

Windows NT, Win32, Windows 95 and Windows 98 are registered trademarks of Microsoft  Corporation. UNIX is a trademark of Novell Corporation. Cygwin32  is probably a trademark of Cygnus Solutions. OpenNT  is a trademark of Softway Systems.

  
Setting up CygWin for cfengine

Setting up CygWin  is a relatively easy procedure. It provides an Install Shield that does the actual installation for you and then you will have to do some manual configuration to be able to use it with cfengine. It might be a good idea to read through the CygWin  User's Guide in addition to this before installing CygWin .

  1. First you need to download the binary release of CygWin  called full.exe from Cygnus solutions website at http://sourceware.cygnus.com/cygwin/download.html.

  2. When you have the binary on your system, run it. You will be prompted for a location to extract the temporary files needed to install the program (the default should work fine). Then you will be prompted for an install location, which can be any location you would like.

  3. Now you have to create some directories to simulate a UNIX system. In the shell you write the following to create the directories /tmp, /etc and /usr.
    $ mkdir /tmp /etc /usr
    

  4. You also need a directory /bin which is a symbolic link that points to CygWin's  bin directory. Writing something like this in the shell should do the trick:
    $ ln -s /bin /path/bin
    

  5. Cfengine needs to find the files  /etc/passwd and /etc/group which can be generated from the information on the NT system. To set this up execute the following commands in the shell:
     
    $ mkdir /etc 
    $ mkpasswd -g > /etc/passwd 
    $ mkgroup > /etc/group
    
    These files are static and will not be updated when the system information changes. You will need to execute these commands again to update the files.

  6. For everything to function some variables need to be set as well. You need to set the HOME variable to your home directory. The variable MAKE_MODE should be set to UNIX for the make  program to work properly. Write the following:
      
    $ export HOME=/homedir 
    $ export MAKE_MODE=UNIX
    

  7. To make cfengine able to use the chown command the CYGWIN variable needs to be set to ntea (NT Extended Attributes). This should be done in the file cygnus.bat. Add the following line in the file:
      
    export CYGWIN=ntea
    

Installing RX-1.5

Cfengine uses regular expressions  and CygWin  did not provide the necessary header files so we needed to install a regular expression  library. We chose to use the RX-1.5 library. To compile  this library follow the steps listed below.

  1. First you need to get a hold of the source. It can be obtained from http://ftp.gnu.org/gnu/rx/.

  2. Extract the files  from the tar.gz file by using winzip or tar. To extract the files  with the tar program you write something like:
      
    $ tar xfz rx-1.5.tar.gz
    

  3. The configure  script tries to locate the program tsort.exe which does not come with the CygWin  library. To work around this we make a symbolic link named tsort.exe that points to cat.exe in CygWin's bin directory. Cat works just as fine as tsort. Write the following command to make the link:
      
    $ ln -s tsort.exe cat.exe
    

  4. If the directory /usr/local doesn't exist you must create it. Use these commands:
      
    $ cd /usr
    $ mkdir local
    

  5. Now you can run configure , make  and finally make  install. In the shell you do it like this:
      
    $ configure
    $ make
    $ make install
    

You should now be able to compile  cfengine successfully.

Added code

This is meant to be a complete documentation for the extended functionality of cfengine, i.e. the code that is written to make cfengine run under Windows NT. It should serve to increase any future software developers' comprehension of our work, assisting them if attempting to extend the current functionality of cfengine on NT. Furthermore, the authors assume that the readers of this documentation have a solid understanding of C programming.

We would like the reader to note that the program itself is so well commented that it should have the ability of serving as documentation in itself. In addition to the extensive use of comments, we have been careful to use descriptive names on functions and variables to enhance the general understanding of the program. Our intentions are that an experienced programmer should easily be able to understand the code simply by looking at it.

Our code is integrated into an existing version of cfengine. As the new version should work on both UNIX and NT, whilst still being subject to further development, we appreciated the importance of clearly marking our work in the code. We did this to avoid any confusion and to clarify which parts are needed to make cfengine run on NT.

The section Description intends explain the principal doings of our code and to give the reader a basic overview and understanding of the program that we have written as an addition to the existing code in cfengine. To show the connection between the different program modules and how these interact we have tried to explain and show the structural construction of our program sequence in the section on Structure and functionality. An extensive explanation of every function that we have coded will be provided in Section [*]. We have also made some changes to functions and structures that already existed in cfengine. These changes will be described and explained in the section Other changes. Our code has also been subject to extensive testing. This is described in the section Testing the code.

Description

The purpose of our code is to implement cfengine's file permission functionality on Windows NT. On NT a file's permission is described through the use of Access Control Lists, ACLs. An ACL have a list of ACEs specifying a user. Connected to each user is a mask specifying the permissions that user have on the file. Any user that is not listed in the ACL will be denied every permission to the file. On UNIX systems cfengine has the ability of changing the users' permissions on files . To succeed in this on NT, we had to develop code that would enable the manipulation of NT's ACLs. This was a challenging task and has been difficult to implement because of the complexity of NT's ACLs as well as the lack of documentation in this area. The code does not work as intended as of yet, however it is close to performing the desired task. Our code may seem intricate at times. This is due to the complexity of the job at hand. Hopefully the next sections will clarify things a bit.

Structure and functionality

As an attempt to take you through the code, step by step, we will explain how the code that we have added successfully can manipulate the permissions on a file on Windows NT.

The method of permission-manipulation is set in the configuration file. If the method is overwrite we create a new ACL and add an ACE  for every user that exists in cfengine's configuration file, making sure that all the Access Denied ACEs  comes first. If the method is append we add the existing users as well as those existing in the config file. We make sure that the rights of the users that already existed in the ACL are changed if any of those users also existed in the configuration file. Finally, no matter the method of manipulation, the new ACL is set to be a part of the file's security descriptor. The permissions that the users had on the file has now successfully been altered.

To give you an understanding of the structure of our code we have provided you with a figure showing how our functions tie together. See Figure [*]. The construction should be relatively simple and easy to understand. If a deeper understanding of the code and functionality is required, please see the description of the functions in Section [*] and  [*].
   Figure: The structure of our code. Starting with CheckNTACE.

[IMAGE png]

   
New functions

We have programmed several functions in an attempt to implement cfengine's file permission functionality on NT. Although this goal has not yet been reached, the structure of the program and the underlying logic leaves us very close to the desired effect. Fulfilling the task of implementing file permissions on cfengine for NT should be a relatively simple task. Each of the functions that we have coded will here be explained in detail. Note that for any of our code to work, it is essential that the file windows.h be included at the top of the file acl.c, which is located under cfengine's src directory. The file acl.c includes every single function that we have coded from scratch.

getNTModeMask


[IMAGE png]

This function is called from the functions appendNTACEs and createNTACL.

The function handles the conversion from the UNIX like mode (like rwx) to NT's access mask. The string containing the desired mode can use the plus-, minus-, comma- and equal signs to add or remove rights. This is the way cfengine worked on UNIX systems before, i.e. no functionality is lost there. The string can be any combination of the rights r,w,x,d,p, and o, as well as cfengine's defined words (default, all). As a slightly extended functionality, the function will also interpret NT's predefined words (read, change, all), but only if those words are used alone.

For simple conversion from a mode to an access mask, let old_mode be 0. Otherwise bitwise manipulation will be performed to merge the two permissions. How the permissions will be merged will depend on new_mode.

The function starts by checking for the existence of any words in new_mode. It does not test for the word noaccess because this is already taken care of when parsing the configuration file. Please see the description of the function AddACE under the section on other changes. If the word default is found, the user should be removed from the ACL and the function returns 0. This value is remembered and later checked in the function attachToNTACEs to ensure that users are removed (or rather not added) if the value is 0. If the words all, read or change is encountered the respective masks are returned. After these tests we know that the string is put together by a set of characters representing modes. Bitwise manipulation will be needed.

Whether rights are to be added or removed is decided by a flag. The flag says to add by default. A loop checks every character in the string. If a plus or a comma sign is encountered, the flag is set to add, whilst if a minus sign is encountered the flag is set to remove. The character representing a mode will decide which bits to set in (or remove from) the mask. If an equal sign is encountered, the existing mask is set to zero and the flag is set to add before continuing reading characters from the string. The loop continues until the last character is processed.

At the end of the function the mask is checked. If it has every single specific right set (rwxdpo) we return the GENERIC_ALL constant which will cause the system to show the user with All rights set.

getNTACEs_Size


[IMAGE png]

This function is called by the function createNTACL.

The purpose of this function is to calculate the size needed to hold the ACEs  that will be created if one attempts to create one for each of the CFACEs . This function will only work for CFACEs  on NT, because it requires that the access type is set in the CFACE  struct.

The function starts by iterating through the list of CFACE  structs. Structs that has no name (i.e. no user), structs where the mode is set to default or structs where the class is to be excluded are simply skipped. After this test we attempt to find the size of the user's security identifier (SID) by calling NT's function LookupAccountName. If the size of the SID is 0, the user does not exist and the function skips that struct. If the SID actually does have a size, we store it together with the size needed to hold the ACE , which by default has one DWORD reserved for the SID. Hence the size calculated for each struct with a valid user is the size of the users SID plus the size of the ACE  minus a DWORD. The accumulated size for all the structs is returned by the function.

getNTACLInformation


[IMAGE png]
q

This function is called by the function createNTACL, but only if the method of fixing the file's permission is append.

The function was created to improve the structure of the program and to make it more readable, in particular the function createNTACL. The purpose of this function is to discover whether the file specified by filename actually has an SD  and an ACL . If it does, we retrieve information about the acl  and update the pointer sent to the function (old_pacl). Remember to free the memory pointed to by the pointer that is returned!

attachToNTACEs


[IMAGE png]

This function is called by the functions appendNTACEs and createNTACL.

The purpose of this function is simple. By sending an existing list of CFACEs  and the information needed to create a new CFACE  struct, the function will create a new struct with the relevant information and append that struct to the end of the existing list.

Before actually creating a new struct, the functions checks that the NTMode is not 0. If it is, it has been set to 0 by an earlier call to the function getNTModeMask because the user's mode was set to default. Unless NTMode is 0, memory is allocated for the new struct, and the relevant information is copied into the struct.

appendNTACEs


[IMAGE png]

This function is called by the function createNTACL.

This function has one clear goal. It should enable the appending of users to those already existing in a file's ACL . After calling this function, all existing ACEs  should be added to new_aces, possibly with altered permissions if the users that already existed in the file's ACL  also existed in the config file.

The function first uses oldACLSize to get the number of ACEs  in the existing ACL , and then it iterates through every ACE in the ACL , using the pointer old_pacl to get each one of the ACEs . For every ACE  the function must check if the user exists in the configuration file as well, because if he does the permissions must perhaps be altered.

If the user does exist in the config file and the access type given in the config file matches the access type on the existing ACE, the new access mask representing the altered permissions is retrieved by calling getNTModeMask. The mode given in the config file and the mask from the existing ACE  will then be used as parameters to getNTModeMask. Then a call to the function attachToNTACEs will hook the user with the new rights to new_aces.

If the existing ACE's  username and access type does not match any of the ones in the config file, no alterations on the user's permissions need to be done. A call to attachToNTACEs hooks the user up new_aces with the information already existing in the ACE. The UNIX like mode is omitted, however, and the classes is simply set to be any as we cannot retrieve this information from NT's ACEs .

After iterating through all the existing ACEs , every user that did not already exist in the config file with mode set to default should be a part of new_aces with the correct access mask set, whether the existing ACEs  had their permissions altered in any way or not. After calling this function, one should only need to add the users in the config file that has not yet been accounted for to new_aces. Then one should be ready to create a new ACL  with an ACE  for every struct in new_aces.

AddNTACEs


[IMAGE png]

This function is called by the function createNTACL.

We call this function when a new list of CFACEs  containing every user that is to have their own ACE  in the new ACL  finally exists. This list is sent to the function as new_aces. Before calling this function, a new ACL  must be initialised with the correct size. A pointer to this ACL  must then be passed as the parameter new_pacl. Because the order of the ACEs  is of great importance, as we have explained earlier, the preferred access type of the ACEs  is passed as a parameter to the function. This way, we can call this function twice. Once to create all ACEs  with access type denied, then a second time to add all ACEs  that are allowed ACEs .

The function starts by iterating through the list of CFEACE structs. For each struct we retrieve the name of the user and attempt to get the size of the user's SID by calling NT's function LookupAccountName. If the SID does have a size, we know that the user exists. We then allocate memory to hold the SID and retrieve it by calling LookupAccountName a second time. We now add an ACE  to the ACL  pointed to by new_pacl. The ACE  is added by a call to either AddAccessAllowedAce or AddAccessDeniedAce depending on the value of the parameter accessType. If the accessType is empty (i.e. not specified in the config file) AddAccessAllowedAce will be called, thus implementing Access Allowed ACE  as the default type of ACE . The reasons for choosing Access Allowed ACE  as the default ACE  type is explained in an earlier chapter. Before moving on to check the next struct in the list we are iterating we free the memory held by the user's SID so that no memory is lost.

createNTACL


[IMAGE png]

This function is called by the function CheckNTACE and is the main controlling function, tying the rest of the functions together.

First of all, the function creates a pointer to an empty list of CFACEs . Potentially, this list will contain every user that is to have his own ACE  in the ACL . It then checks the method.

If the method is a, i.e. append, we call getNTACLInformation to get information about the file's existing ACLindexACL. We send a flag to the function, and if the flag is raised when the function returns we know that the file actually had an ACL . If this is the case we call appendNTACEs to append the file's existing ACEs  to our empty list of CFACE  structs. If the file failed to have an ACL  and cfengine is running in verbose mode we let the user know that the file has unrestricted access.

If the method was append we should now have all the file's existing users added to our list of new CFACEs  with possibly modified rights. If the method was not append, that list should still be empty. Whatever the case is, the function continues with an iteration of the CFACE  list aces containing structs with the information from the config file. Users in this list with an access mask equal to zero (as initialised) will be appended to our new list of CFACE  structs because they have not yet been accounted for. Users with an access mask that has a value other than zero have already been processed in the function appendNTACEs. After the iteration of aces every existing user with modified permissions as well as new users specified in the configuration file should have a struct in our new list of CFACEs .

Not all of the users in our new list of CFACEs  will get an ACE . Either because they have their mode set to default or because the user is not defined as a user on NT. But our function getNTACEs_Size checks this and returns the size needed to hold all the ACEs  that can in fact be created. So we call getNTACEs_Size and get the size needed to hold the ACEs . Then we add the size of the ACL  itself and allocate memory for the new ACL  with all it's potential ACEs . We then initialise the file's security descriptor  and the new ACL .

Now everything is ready to add ACEs  to the new ACL . However, as mentioned several times before, the order of the ACEs  is important. We call our function addNTACEs once to add all the Access Denied ACEs  to our new ACL , and then we call it again to add all the Access Allowed ACEs . Then we put the new ACL  into the file's security descriptor  (SD ) by calling NT's function SetSecurityDescriptorDacl. After checking that the SD  is still valid we install it, in effect connecting it to the file.

At the end of the function we free all the memory that we have allocated earlier and return.

CheckNTACE


[IMAGE png]

This function is called by a function that already existed in cfengine, but that we have modified to include NT. The function is called checkACLs.

This function does nothing but call createNTACL. This implies that this function is not needed, and strictly speaking it really is not necessary. We still chose to have it to follow the naming convention that seem to already exist in cfengine. So now, depending on the operating system, checkPosixACE, checkDFSACE or checkNTACE will be called from the function checkACLs.

   
Other changes

In order to implement cfengine's file permission and acl  functionality on NT, we not only had to code new functions. We also had to change some of the existing code in cfengine to make it behave the way we wanted if it was running on NT. The changes we made to existing functions and structs are listed here. Note also that the file windows.h had to be included in the file acl.c located under cfengine's src directory. This was done to be able to use NT's own functions to manipulate a file's permissions.

CFACL


[IMAGE png]

The struct is defined in the file cf.defs.h located under cfengine's src directory.

We have added the variable nt_acltype in case NT is defined, i.e. the variable will be defined when cfengine is run under NT. Windows NT has defined two types of ACLs , system  and discretionary . We have been consentrating on discretionary ACLs  (DACLs ). However, sometime in the future it may be appropriate to implement the SACLs  as well. The variable we have defined here is not yet in use, but is implemented for future use.

CFACE


[IMAGE png]

The struct is defined in the file cf.defs.h located under cfengine's src directory.

NT operates with both users and groups but they all have their own security identifier, a unique number. This makes the variable acltype redundant on NT. We do not use it in our code, and the value of it (which is specified in cfengine's configuration file) is of no importance.

We have added the variables access and NTMode in case NT is defined, i.e. the variable will be defined when cfengine is run under NT. Windows NT has defined several different types of ACEs  but since we have concentrated on discretionary ACLs  (DACLs ) we have only implemented the two types of ACEs  used by the DACL , which are denied and allowed. Th NTMode is included to hold NT's own access mask representing a user's permission on a file. We added this variable to obtain the most effective and optimised code when implementing cfengine's append functionality on a file's permissions.

AddACE


[IMAGE png]

This function is coded in the file acl.c, which is located under cfengine's src directory. It is called for each of the lines in the acl  definition in cfengine's configuration file, i.e. each of the lines with a colon in the example configuration file as follows:

Here comes "cfengine.conf.acl"

The line is sent as parameter string. This function then allocates memory for a CFACE  struct and parses that string, storing the information in the struct. We made some additions to this function to implement NT functionality. We added a new variable and enabled parsing of an additional variable at the end of the line represented in string. The line would usually look something like:

user:kjenslj:rwx

But if cfengine is run on NT, one should be able to specify the type of ACE  to be created for that user as well. This is explained in an earlier chapter. If cfengine is run on NT, the new look of the line can be something like:

user:kjenslj:rwx:allowed

The function will still parse it correctly, saving the new access type variable in an additional variable that we have created in the CFACE  struct.

If NT is defined we also check if the mode is noaccess. If this is the case, the mode saved in the struct is all and the respective access type is set to be denied. This way an ACE  with NT's predefined permission type No Access will be created for the respective user on the file.

CheckACLs


[IMAGE png]

This function is coded in the file acl.c, located under cfengine's src directory.

When using the files  command in cfengine's configuration file, cfengine will after having parsed the configuration file and found that an acl  definition is specified eventually reach this function. We have added a test in this function, checking whether or not the file system is NT, and if it is we call our function CheckNTACE.

Testing the code

The code we have written as well as the design of our module has been under constant evaluation. We planned the structure of our program module before starting to implement it in the already intricate system of cfengine. This did not prevent the occurrence of one major restructuring halfway through the project. This happened because we had failed to consider cfengine's extended functionality of removing existing rights when planning the construction of our code.

For every new functionality that we have implemented, we have performed exhaustive testing through extensive use of cfengine's configuration file, in effect attempting every possibility and combination of input. An analysis of the respective output would repeatedly trigger further investigation of our module's performance under different conditions. Whether those conditions be faulty input from the configuration file, lack of input, unlikely combinations of input, or any other exceptional event, we would continuously work to improve the code. We believe that as of today, most combinations have been tested for an corrected in case of faulty performance. The things we have tested is listed at the end of this section.

Today there are only a few cracks that we are aware of. These flaws in our code have nothing to do with the functionality of the code, but has only an effect on the internal performance of the module, such as possibly allocating too much memory creating too many ACEs . The user will not be aware of these bugs as the program still behaves as expected. Only time has prevented us from correcting them.

Testing of the code has showed us that full manipulation of ACLs in directories not yet works as desired. However, as time has not permitted us of implementing this functionality, we have chosen to not see this as a fault in the code but as a feature yet to be implemented.

The behaviour of our module in the event of an error has also been tested extensively. We have tried to follow the existing convention on error handling, calling previously coded functions to handle error messages. The error messages themselves are also meant to follow an existing convention, producing information as in which module the error occurred, the effect it had and the result. Depending on the seriousness of the error the program will either exit or log the error to a file.

As of today, our code is not yet complete. It should include the complete functionality on file permissions on NT as well as enabling editing the registry  and the implementation of UNIX' ps  command. The fact that we are missing complete and fully functional code has prevented us from further testing the code. When the ACLs are fully implemented, an extensive and complete test of the functionality should of course be executed, although we hopefully have discovered and corrected all exceptions by that time.

The tests

We have tested cfengine's behaviour on ACLs under the following conditions:

Specifying a user that does not exists triggered an error message before cfengine continued. Nothing was done for the user that did not exist.

Omitting to specify the access type should make cfengine interpret the access type as allowed. This was also the case. Specifying an access type other than the possible values allowed or denied resulted in an error message before cfengine continued. Nothing was done for the user that had a faulty access type.

Specifying permissions that do not exist will trigger an error message before cfengine continued. Nothing was done for the user with the faulty permissions.

Specifying a method or a file system that is invalid is taken care of by existing code in cfengine.

The next two sections documents how our code responds to the two methods of working with ACLs on NT:

Overwrite method

We tried to grant a user every possible combination of the permission bits as well as the words noaccess, default, read, change and all. Using the sign - to remove permissions makes no sense when overwriting, so the code should not make an ACE  for that user. We tested this and it works. The other signs, + and = and , were tested extensively and seemed to always work as desired.

Denying rights was more difficult to test as NT 4.0 is unable to show denied permissions in their graphical user interface. That is except for the times when a user is denied all rights. Then that user is showed having No Access. We tried this and it worked. Denied rights can be tested much more extensively in a network, but we do not have a network with NT machines available, and neither do we have the time it would require to do it. We assume that denying specific rights also works as the code is practically identical to when granting those rights.

Append method

The tests here were performed on files with existing users. We were trying to alter existing users' permissions on a file. To test every possible combination of permissions on every possible combination of existing permissions would be an enormous job, practically impossible to carry through. We did, however, try to grant a user every possible combination of the permission bits as well as the words noaccess, default, read, change and all on a large number of existing users. It worked in every case. We tested the sign - extensively, and when removing all rights the user was still in the ACL but with No Access. In other words as desired. The signs + and = and , were also tested and seemed to always work as desired.

What was said about denied rights in the previous section Overwriting method applies here two. The only difference was that we were able to test it a little bit more. We first denied some permissions to a user on a file. We could not see the results of that operation because of NT's limitation on it's graphical user interface (GUI). However, when we ran cfengine again we denied the same user the rest of the permissions. The result was as desired. The user had been denied all rights, and this is shown by NT as No Access in the GUI. We tried this several times, and it always worked. This implies that the denied functionality works the way it is supposed to.

Bugs or possible improvements

There will always be a presence of errors and bugs in functional code. Because of the timespan of this project we were unable to complete all the tasks that must be resolved in order for cfengine to run NT. Since the project as of today is not finished, our code definitely contain a few bugs, and there are also some performances that we would have wanted to improve. This section will attempt to clarify some of the improvements that might be implemented in the future, as well as the bugs that we are aware of in our code. They have not been corrected as time has not permitted us.

Default rights for administrator

When using the overwrite method, the file's existing ACL will be replaced with a new one. The new ACL will be based on the information given in cfengine's configuration file. However, if the administrator or the operating system (username system) is omitted from the configuration file, they will not get any permissions on the file. It of course essential that the administrators always have full access to any file on the system. We believe that cfengine should have the functionality of automatically including the administrator with all permissions in the list of users that should have access to the file. This should always be the case to protect the system from the possibly occurrence of catastrophic events.

Incomplete functionality of the config file

In the configuration file one specifies a type of action, such as fixall or warnall. We have not had time to implement checking the value of the action, though. Our code today runs as if fixall was specified.

In the configuration file, the manual says that the type of acl, the acl_type, is to be specified as either user or group. However, we never test the value of acl_type because it does not matter. All we need to create an ACE  is the name of the user/group to find the respective security identifier (SID). The value of acl_type is therefore irrelevant, but we have not tested what happens if something other than user or group is specified. This is something to be checked out.

A star/asterisk in the field for user/group would normally imply that the ACL applies to the owner of the file object. However this functionality is as of today not yet implemented. It should not be a problem to fix this, though.

Missing or faulty test conditions

We have assumed that every string in the config file is written with lowercase characters. If this is not the case the result may be that the program does not function as expected. Converting every word to words in lowercase is an easy task and will solve the problem.

In the function getNTModeMask we check if the mode is noaccess. This test may be omitted though because this is checked already when parsing the configuration file. If noaccess is the mode then all will be stored in the CFACE with the access type set to denied, thus making the test in getNTModeMask redundant.

When checking the existing ACEs from the old ACL against the users specified in the config file, we stop testing when a user with the same name is found (in appendNTACEs). Then the mask is corrected and saved. However, it is possible that several users with the same access type have been specified in the configuration file. This should cause the mask of the original ACE to be manipulated further, but this is not the case today. We should not break the loop that iterates through every user in the config file when a match is found, but continue to check the old ACE against every single user.

When the overwrite is used, or after the users that existed has been modified if append is used, there is a small bug. We now iterate through the list of the users from the config file, adding every one that has a mask=0 to the new list that specifies who should get an ACE in the new ACL. However, if several users with the same name occurs several times, that user will also be added several times. This should not happen, although NT seems to be able to handle it. We do not know the exact effect even it seems to be OK. Besides, in any case we are wasting space. We should always check the user that is to be added to our new list against every user that already exist in that new list.

Since we learned about the directories, we believe that the function returning the access mask (getNTModeMask) must be modified. Today, the mask returned will use the specific bits of the mask. However, when working with directories it is sometimes the case that the generic part of the mask should be used instead of the specific. We believe that an inheritance flag located in the ACE header decides this. If so, the flag should be sent to the function deciding which part of the mask we should use when setting the bits in it.

User manual

This is meant to be a manual in addition to the already existing manual on cfengine. The cfengine manual can be downloaded from http://www.iu.hioslo.no/cfengine/. Our addition consist of an explanation on how to use the ACL functionality when running cfengine on NT, in effect an extension of Chapter 8.1 on ACL's in the manual. To increase the understanding of how to use the acl command in cfengine's configuration file, we have included the section on ACL's and one of it's subsections from the existing manual. For a complete reference, please see the existing manual.

This manual is meant to be sufficient for you to successfully use ACL's on cfengine for NT, but will not provide you with much understanding. For a fuller comprehension we recommend that you read the previous chapters in this report.

Access Control Lists (ACLs) on cfengine

The rest of this section is a copy of section ``8.1 acl'' from cfengine's existing manual.
   acl:

      class::

         { acl-alias

         action
         }
Cfengine's ACL feature is a common interface for managing file system access control lists (ACLs). An access control list is an extended file permission. It allows you to open or close a file to a named list of users (without having to create a group for those users); similarly, it allows you to open or close a file for a list of groups. Several operating systems have access control lists, but each typically has a different syntax and different user interface to this facility, making it very awkward to use. This part of a cfengine configuration simplifies the management of ACLs by providing a more convenient user interface for controlling them and--as far as possible--a common syntax.

An ACL may, by its very nature, contain a lot of information. Normally you would set ACLs in a files  command, or a copy  command. It would be too cumbersome to repeat all of the information in every command in your configuration, so cfengine simplifies this by first associating an alias together with a complex list of ACL information. This alias is then used to represent the whole bundle of ACL entries in a files  orcopy  command. The form of an ACL is similar to the form of an editfiles  command. It is a bundle of information concerning a file's permissions.

      { acl-alias

       method:overwrite/append
       fstype:posix/solaris/dfs/afs/hpux/nt

       acl_type:user/group:permissions
       acl_type:user/group:permissions
       ...
      }
The name acl-alias can be any identifier containing alphanumeric characters and underscores. This is what you will use to refer to the ACL entries in practice. The method entry tells cfengine how to interpret the entries: should a file's ACLs be overwritten or only adjusted? Since the file systems from different developers all use different models for ACLs, you must also tell cfengine what kind of file system the file resides on. Currently only solaris  and DCE/DFS ACLs are implemented.

NOTE: if you set both file permissions and ACLs the file permissions override the ACLs.

  
Access Control Entries (ACEs) on cfengine

The rest of this section is a copy of section ``8.1.1 Access control entries'' from cfengine's existing manual.

An access control list is build of any number of individual access control entries (ACEs ). The ACEs  has the following general syntax:

       acl_type:user/group:permissions
The user or group is sometimes referred to as a key.

For an explanation of ACL types and their use, refer to your local manual page. However, note that for each type of file system, there are certain entries which must exist in an ACL. If you are creating a new ACL from scratch, you must specify these. For example, in solaris  ACLs you must have entries for user, group and other. Under DFS you need what DFS calls a user_obj, group_obj and an other_obj, and in some cases mask_obj. In cfengine syntax these are called user:*:, @other:*: and mask:*:, as described below. If you are appending to an existing entry, you do not have to re-specify these unless you want to change them.

Cfengine can overwrite (replace) or append to one or more ACL entries.


[IMAGE png]

If the new ACL exactly matches the existing ACL, the ACL is not replaced.

The individual bits in an ACE  may be either added subtracted or set equal to a specified mask. The + symbol means add, the - symbol subtract and = means set equal to. Here are some examples:

           acltype:id/*:mask

           user:mark:+rx,-w
           user:ds:=r
           user:jacobs:noaccess
           user:forgiven:default

           user:*:rw
           group:*:r
           other:*:r

The keyword noaccess means set all access bits to zero for that user, i.e. remove all permissions. The keyword default means remove the named user from the access control list altogether, so that the default permissions apply. A star/asterisk in the centre field indicates that the user or group ID is implicitly specified as of the owner of the file, or that no ID is applicable at all (as is the case for `other').

NT ACLs

As a change to the existing general syntax, the ACEs  can on NT be written as follows:
       acl_type:user/group:permissions:accesstype

The actual change consists of the extra field containing the access type. A star/asterisk in the field for user/group would normally imply that the ACL applies to the owner of the file object. However this functionality is as of today not yet implemented.

In NT, the ACL type can be one of the following:

           user
           group
Both types require that you specify the name of a user or a group.

NT permissions are comprised of the bits 'rwxdpo', where:

           r - Read privileges
           w - Write privileges
           x - Execute privileges
           d - Delete privileges
           p - Privileges to change the permissions on the file
           o - Privileges to take ownership of the file
In addition to any combination of these bits, the word noaccess or default can be used as explained in the previous section. NT comes with some standard, predefined permissions. The standards are only a predefined combination of the different bits specified above and are provided with cfengine as well. You can use the standards by setting the permission to read, change or all. The bit implementation of each standard is as on NT:
           read   - rx
           change - rwxd
           all    - rwxdpo
where the bits follow the earlier definition. The keywords mentioned above can only be used alone, and not in combination with +, -, = and/or other permission bits.

NT defines several different access types, of which only two are used in connection with the ACL type that is implemented in cfengine for NT. The access type can be one of the following:

           allowed
           denied
Intuitively, allowed access grants the specified permissions to the user, whilst denied denies the user the specified permissions. If no access type is specified, the default is allowed. This enables cfengine's behaviour as on UNIX systems without any changes to the configuration file. If the permissions noaccess or default is used, the access type will be irrelevant.

ACL Example

Here is an example of a configuration file for an NT ACL:

control:
        actionsequence = ( files )
        domain = ( iu.hioslo.no )

files:
        $(HOME)/tt    acl=acl_alias1    action=fixall

acl:
        { acl_alias1

        method:overwrite
        fstype:nt

        user:gustafb:rwx:allowed
        user:mark:all:allowed
        user:toreo:read:allowed
        user:torej:default:allowed
        user:ds2:+rwx:allowed

        group:dummy:all:denied
        group:iu:read:allowed
        group:root:all:allowed
        group:guest:dpo:denied
        }

No References!


Index

access control entry
see ACE
access control list
see ACL
ACE
Access control lists - | Access control lists - | Access control lists - | no title | Granting access | Deciding access | The config file | Appending ACEs to an | Extended functionality | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | NT's access mask | Default | Default | Creating ACLs with denied | Creating ACLs with denied | Directories | Directories | Directories | Directories | Directories | Directories | Directories | Thoughts on NT security | appendNTACEs | appendNTACEs | appendNTACEs | appendNTACEs | appendNTACEs | appendNTACEs | appendNTACEs | appendNTACEs | AddNTACEs | AddNTACEs | AddNTACEs | AddNTACEs | AddNTACEs | AddNTACEs | createNTACL | createNTACL | createNTACL | createNTACL | createNTACL | createNTACL | createNTACL | AddACE | Testing the code | Overwrite method | Incomplete functionality of the | no title | Access Control Entries (ACEs) | Access Control Entries (ACEs) | Access Control Entries (ACEs) | NT ACLs
access allowed
Access control entries - | Access control entries - | Access control entries - | Granting access | Deciding access | Deciding access | Implementing ACLs in cfengine | Implementing ACLs in cfengine | no title | Overwriting an existing ACL | Optimisation and subtraction of | Optimisation and subtraction of | NT's access mask | Creating ACLs with denied | Creating ACLs with denied | Creating ACLs with denied | AddNTACEs | AddNTACEs | AddNTACEs | createNTACL
access allowed object
Deciding access
access denied
Access control entries - | Access control entries - | Access control entries - | Granting access | Deciding access | Deciding access | Optimisation and subtraction of | NT's access mask | NT's access mask | Noaccess | Noaccess | Noaccess | no title | Creating ACLs with denied | Creating ACLs with denied | Creating ACLs with denied | Creating ACLs with denied | Creating ACLs with denied | Creating ACLs with denied | Creating ACLs with denied | Creating ACLs with denied | Structure and functionality | createNTACL
access denied object
Deciding access
access mask in
From mode to NT's | Directories | appendNTACEs
appending to
Implementing ACLs in cfengine
appending to ACL
no title | Appending ACEs to an | Appending ACEs to an | Appending ACEs to an | Appending ACEs to an | Extended functionality | Optimisation and subtraction of | Optimisation and subtraction of | Directories | AddNTACEs
default
AddNTACEs | AddNTACEs
header
Directories
in ACL
Access control entries - | Deciding access | Deciding access | Deciding access | Overwriting an existing ACL | Optimisation and subtraction of | Optimisation and subtraction of
in cfengine
Parsing the config file
in config file
Preparations | The config file | The config file | Parsing the config file
in DACL
Discretionary ACL - DACL
in SACL
System ACL - SACL
information in
Access control entries -
inheritance flag in
Optimisation and subtraction of
inherited
Deciding access | Deciding access | Deciding access | Deciding access | Deciding access
non-inherited
Deciding access
on NT
The config file | Appending ACEs to an | Appending ACEs to an | Appending ACEs to an | Optimisation and subtraction of | Directories | appendNTACEs
on Solaris
Using the CFACE struct
order of
no title | Deciding access | Deciding access | Optimisation and subtraction of | Creating ACLs with denied | Structure and functionality | createNTACL
permissions in
Access control entries -
removing from ACL
Optimisation and subtraction of | Default | Default
size of
Creating ACLs with denied | getNTACEs_Size | getNTACEs_Size | getNTACEs_Size
types of
Access control entries - | Access control entries - | CFACE | CFACE | AddACE
ACL
UNIX vs. NT | UNIX vs. NT | UNIX vs. NT | UNIX vs. NT | File permissions | File permissions | Security descriptors - SDs | Security descriptors - SDs | no title | Access control entries - | Granting access | Granting access | Implementing ACLs in cfengine | Implementing ACLs in cfengine | Research | Creating ACLs with Access | Overwriting an existing ACL | Appending ACEs to an | Appending ACEs to an | Appending ACEs to an | Extended functionality | Optimisation and subtraction of | Default | Directories | Directories | Directories | getNTACLInformation | getNTACLInformation | appendNTACEs | appendNTACEs | appendNTACEs | appendNTACEs | createNTACL | createNTACL | createNTACL | Other changes | CheckACLs
ACEs in
Deciding access | Deciding access | Optimisation and subtraction of | Optimisation and subtraction of | AddNTACEs | createNTACL
adding ACEs
Appending ACEs to an
adding ACEs to
Optimisation and subtraction of | Optimisation and subtraction of | Directories | AddNTACEs | createNTACL | createNTACL
appending
no title | Appending ACEs to an | Optimisation and subtraction of | Default
appending ACEs
Implementing ACLs in cfengine
audit
System ACL - SACL
create a file with
Overwriting an existing ACL | Overwriting an existing ACL
creating
Implementing ACLs in cfengine | Preparations | no title | appendNTACEs
defining
The config file | AddACE
directory
Directories
discretionary
no title | Discretionary ACL - DACL | CFACL | CFACL | CFACE
editing
Implementing ACLs in cfengine
files/directories
Directories
for directory
Directories | Directories | Directories
for file
Directories
implementing
Conclusions | Conclusions
implementing in cfengine
no title | Implementing ACLs in cfengine
in cfengine
Preparations
in SD
Overwriting an existing ACL | Appending ACEs to an | Optimisation and subtraction of
incfengine
Preparations
initialising
Overwriting an existing ACL | Appending ACEs to an | AddNTACEs | createNTACL
manipulating
System ACL - SACL | Deciding access
memory needed
Overwriting an existing ACL | Overwriting an existing ACL | Creating ACLs with denied | Creating ACLs with denied | Creating ACLs with denied | createNTACL
modifying
Directories
not in SD
createNTACL
on NT
Research | Directories | Thoughts on NT security | Thoughts on NT security
on Solaris
Research | Research | The config file
order of ACEs in
Creating ACLs with denied
overwrite
Creating ACLs with Access
overwriting
Creating ACLs with Access | no title | Overwriting an existing ACL | Overwriting an existing ACL | Overwriting an existing ACL
pointer to
AddNTACEs
removed from
see default
removing ACE from
Optimisation and subtraction of
removing ACEs from
Default
size of
Access control lists -
structure
Access control lists -
system
no title | CFACL
two types
CFACL
types of
Access control lists -
updating
Optimisation and subtraction of
with denied ACEs
no title
ACL in
SD
Security descriptors - SDs
CFAC E struct
Parsing the config file
CFACE struct
Overview of cfengine | The config file | no title | Using the CFACE struct | Using the CFACE struct | Creating ACLs with Access | Creating ACLs with Access | Overwriting an existing ACL | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Optimisation and subtraction of | Default | Default | Default | Noaccess | Noaccess | Creating ACLs with denied | getNTACEs_Size | getNTACEs_Size | getNTACEs_Size | getNTACEs_Size | attachToNTACEs | attachToNTACEs | AddNTACEs | createNTACL | createNTACL | createNTACL | createNTACL | createNTACL | createNTACL | createNTACL | AddACE | AddACE
changes to
Parsing the config file | Optimisation and subtraction of
compile
Why cfengine on NT | Overview of cfengine | Choosing a porting layer | Choosing a porting layer | Desired functionality | UNIX vs. NT | Running the configure script | Running the configure script | Running make | Timezone name | Timezone name | Testing cfengine | Copy | Regular expressions | Using the CFACE struct | Conclusions | Installing RX-1.5
configure script
Overview of cfengine
gcc
Desired functionality
make program
Overview of cfengine | Running make
makefiles
Running the configure script
on UNIX
Compilation
rx library
Regular expressions | Installing RX-1.5
with porting layer
Compiling functional code on
configure script
Overview of cfengine | Overview of cfengine | Compilation | Compilation | no title | Running the configure script | Running the configure script | Running the configure script | Regular expressions | Regular expressions | Regular expressions | Regular expressions | Installing RX-1.5 | Installing RX-1.5
copy
Overview of cfengine | Testing cfengine | Links | no title | Cfengine's role on NT | Cfengine's role on NT
from server
Copy | Copy | Copy
local
Copy
setting ACLs with
Access Control Lists (ACLs) | Access Control Lists (ACLs)
CygWin
see porting layers
DACL
Access control lists - | no title | Discretionary ACL - DACL | System ACL - SACL | CFACL | CFACE
ACEs in
Discretionary ACL - DACL
not in SD
Discretionary ACL - DACL
types of ACEs
CFACE
disable
Testing cfengine | no title | Disable
editfiles
Testing cfengine | no title | Editfiles | The registry | The registry | The registry | Access Control Lists (ACLs)
FAT
see file systems
file systems
FAT
File permissions
NTFS
File permissions
files
and cfengine
UNIX vs. NT
cfengine command
Overview of cfengine | Testing cfengine | no title | CheckACLs
cfengine's
Overview of cfengine | Overview of cfengine | Running the configure script
configuration
UNIX vs. NT
copy
Cfengine's role on NT
delete
Cfengine's role on NT
extracting
Installing RX-1.5 | Installing RX-1.5
permissions
UNIX vs. NT | Copy | Files | Access control | Security descriptors - SDs | Directories | Thoughts on NT security | Cfengine's role on NT | Description
required by cfengine
Setting up CygWin for
setting ACLs with
Access Control Lists (ACLs) | Access Control Lists (ACLs)
system
Overview of cfengine | Cfengine's role on NT | Cfengine's role on NT | Cfengine's role on NT
fstab
see mounting
links
Tidy | Disable | Links
cfengine command
Testing cfengine | no title | Links
CygWin's
Links
multiple
Links
single
Links
symbolic
Links
make program
Overview of cfengine | Overview of cfengine | Overview of cfengine | Compilation | Compilation | no title | Running make | Running make | Regular expressions | Regular expressions | Regular expressions | Setting up CygWin for | Installing RX-1.5
distclean
Regular expressions
install
Installing RX-1.5
Microsoft
Access control | Deciding access | NT's access mask | Creating ACLs with denied | Creating ACLs with denied | Trademarks
mkgroup
Owner and group
mkpasswd
Owner and group
mount
see mounting
mounting
no title
by cfengine
UNIX vs. NT
fstab
UNIX vs. NT | Mounting file systems
in CygWin
Mounting file systems
mount command
Desired functionality | Desired functionality
on NT
Desired functionality | Desired functionality | Desired functionality | UNIX vs. NT | UNIX vs. NT | Mounting file systems | Mounting file systems
on UNIX
UNIX vs. NT | UNIX vs. NT
umount command
Desired functionality | Desired functionality
netstat
Desired functionality | Desired functionality
NTFS
see file systems
NutCracker
see porting layers
OpenNT
see porting layers
porting layers
Compiling functional code on
CygWin
Choosing a porting layer | Choosing a porting layer | Choosing a porting layer | Choosing a porting layer | Desired functionality | Desired functionality | Desired functionality | Compilation | Compilation | Running the configure script | Running the configure script | Timezone name | Tidy | Regular expressions | File permissions | Owner and group | Trademarks | Installing RX-1.5
cacls
File permissions
configure script
Running the configure script
downloading
Setting up CygWin for
environment variable
File permissions
file permissions
File permissions | File permissions | File permissions | Access control
free software
Choosing a porting layer
links
Links | Links | Links | Setting up CygWin for
make
Compilation
mount
UNIX vs. NT | Mounting file systems
ps
Process status - ps
regular expressions
Installing RX-1.5
setting up
no title | Setting up CygWin for | Setting up CygWin for | Setting up CygWin for
utilities
Owner and group
NuTCracker
Choosing a porting layer
OpenNT
Choosing a porting layer | Trademarks
UWIN
Choosing a porting layer | Choosing a porting layer | Choosing a porting layer | Choosing a porting layer | Choosing a porting layer | Desired functionality | Desired functionality | Running the configure script | Running the configure script | Regular expressions | Regular expressions
configure script
Running the configure script
make
Compilation
mount
UNIX vs. NT
proprietary
Choosing a porting layer
processes
Desired functionality | Desired functionality | Security descriptors - SDs | Process status - ps | Process status - ps
cfengine command
Testing cfengine
ps
Desired functionality | Desired functionality | no title | Process status - ps | Process status - ps | The registry | Cfengine's role on NT | Testing the code
in NT Resource Kit
Process status - ps
registry
UNIX vs. NT | The registry | The registry
editing
The registry | Testing the code
regular expressions
Editfiles | Regular expressions | Regular expressions
GNU library
Regular expressions | Installing RX-1.5
in cfengine
Regular expressions | Regular expressions | Installing RX-1.5
regexp.h
Regular expressions
rxposix.h
Regular expressions
SACL
Access control lists - | no title | CFACL
ACEs in
System ACL - SACL
SD
see security descriptor | Security descriptors - SDs | Security descriptors - SDs | Overwriting an existing ACL | getNTACLInformation | createNTACL | createNTACL
ACL in
Overwriting an existing ACL | Appending ACEs to an | Appending ACEs to an
security descriptor
see SD | no title | Security descriptors - SDs | Discretionary ACL - DACL | Overwriting an existing ACL | Optimisation and subtraction of | createNTACL | createNTACL
shellcommands
Testing cfengine | no title
Solaris
The config file | The config file | Using the CFACE struct
ACLs on
Research | Research | The config file | Access Control Lists (ACLs) | Access Control Entries (ACEs)
tidy
Testing cfengine | no title | Tidy
umount
see mounting
UWIN
see porting layers

About this document ...

Porting cfengine to Windows NT

This document was generated using the LaTeX2HTML translator Version 99.2beta6 (1.42)

Copyright © 1993, 1994, 1995, 1996, Nikos Drakos, Computer Based Learning Unit, University of Leeds.
Copyright © 1997, 1998, 1999, Ross Moore, Mathematics Department, Macquarie University, Sydney.

The command line arguments were:
latex2html -html_version 2.0 -ascii_mode -split 0 cfengine-NT.tex

The translation was initiated by Paul Visscher on 2001-07-19


Footnotes

... first[+]
If we are able to compile  it with CygWin  it should be unnecessary to try to make it work with UWIN .
... of[+]
Especially if the code is intended to be included in a program such as cfengine, used by administrators on large networks all over the world
... indeed[+]
Only a shame that no proper tool is provided to deal with this complexity, leaving the system administrators with a job that is virtually impossible
... on[+]
Solaris is a type of UNIX system
... DFS[+]
DFS (Distributed File System) is a part of the DCE (Distributed Computing Environment).
...noaccess[+]
Default means that the user should be removed from the ACL  and noaccess means that the user should be denied all access.
...ACL[+]
unless specifically specified through the use of a minus sign or an equal sign.
... functions[+]
included in windows.h
... SIDs[+]
For a closer description of how this is done, see Appendix C
...GlobalAlloc[+]
included in windows.h
... needed[+]
in case a non-existing account was listed in the config file
... mask[+]
see Appendix C for a closer description
... rights[+]
As you quite correctly have managed to figure out, four bits (bit 24 to 28) are left unused
... set[+]
0 ANDed with 1 is 0 and 1 ANDed with 1 is 1
... running[+]
Someone might claim that it is NT that runs cfengine but we beg to differ.


[ English ]

Return to GNU's home page.

Please send FSF & GNU inquiries & questions to gnu@gnu.org. There are also other ways to contact the FSF.

Please send comments on these web pages to webmasters@gnu.org, send other questions to gnu@gnu.org.

Copyright (C) 2001 Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA

Verbatim copying and distribution of this entire article is permitted in any medium, provided this notice is preserved.

Updated: $Date: 2002/08/22 21:26:45 $ $Author: bkuhn $