# HOWTO: Grsecurity quickstarting RBAC roles for Gentoo (x86)

## schmeggahead

HOWTO: Grsecurity quickstarting RBAC roles for Gentoo (x86) Version 0.3

This guide is primarily addressing the Rule Based Access Control (RBAC) within grsecurity enhancements to the kernel.

The current guide is located here: http://www.gentoo.org/proj/en/hardened/grsecurity.xml

The current wiki book for the roles definition is here: http://en.wikibooks.org/wiki/Grsecurity/The_RBAC_System

Note: the wikibook is extensive but it is not obvious how to access various values in the RBAC policy statement. There are links to the value lists but they are not in the table of contents. Inside the "complete formal definition", click on these words to see valid values:

role mode

subject mode

object mode

capability name

resource name

pax flag

resource restrictions

I used this guide to bring up several systems and below is a sequence to start up that worked well for me compared to the dozen or so other more difficult routes to a successful grsecurity RBAC capable system with a minimal access policy.

Prepare

Requirements

use the hardened stage and hardened-sources 

installed sys-apps/gradm package (version I used 2.1.13.200902232204-r1)

bootable system 

kernel with grsecurity enabled (follow the guide section 3 RBAC) (Note: grsecurity is not enabled by default. However, in the hardened kernel configuration menus, many of the options are force selected once grsecurity is enabled. Reading the help on the few remaining choices is not that onerous)

parts of the standard guide regarding learning roles and sysctl NOT completed

So what you have now is a system that is booting with grsecurity enabled in the kernel but the RBAC system is off.

Setup Passwords and Startup Learning

Set your password for the RBAC system. Ensure this is a ridiculously hard password on a production system. On my dev system I make this easy because it is protected and not web accessible and wiped regularly.

```
#/sbin/gradm -P
```

set your admin password:

```
#/sbin/gradm -P admin
```

Note: I like to set an admin password and roles for admin that are more a decoy than the real admin role I use. I name my admin role something like omni and give it the rules admin normally receives. This means that even if the admin password is hacked, they cannot transition to another role and waste their time hacking that, hopefully leaving a trail in /var/log/kern.log such as:

Jan 30 10:55:32 localhost kernel: [75498.299128] grsec: From 192.168.0.3: (root:U:/sbin/gradm) special role admin failure for /sbin/gradm[gradm:9324] uid/euid:0/0 gid/egid:0/0, parent /bin/bash[bash:8846] uid/euid:0/0 gid/egid:0/0

set the init script to start gradm at startup:

```
#nano -w /etc/conf.d/local.start
```

then insert the bottom two lines:

```
# /etc/conf.d/local.start

# This is a good place to load any misc programs

# on startup (use &>/dev/null to hide output)

/sbin/gradm -F -L /gradm.initial.learning.log

#/sbin/gradm -E

```

we'll come back to this setup down further

Note: I did create an init script for init.d and conf.d companion but since this is not a daemon using start-stop-daemon is totally overkill and local.start & local.stop work better.  (thanks Hopeless!) I filed a bug report to have local.start local.stop added to the initscript doc in the handbook.

now add the shutdown to local.stop

```
#nano -w /etc/conf.d/local.stop
```

then insert the bottom line below:

```
# /etc/conf.d/local.stop

# This is a good place to unload any misc.

# programs you started above.

# For example, if you are using OSS and have

# "/usr/local/bin/soundon" above, put

# "/usr/local/bin/soundoff" here.

/sbin/gradm -D

```

[TODO: modify the script to check if RBAC is enabled. Better yet, I would prefer that the shutdown doesn't proceed because it makes a big mess if the RBAC system is enabled and requires resetting the system to restart. Adding all the rules for shutdown opens up a lot of access during normal operation, so I avoid it.]

Create Initial Learned Roles and Manage Learning

Now, ensure that all of the services required on the system are enabled at startup (Note: if you add them in the future, you can repeat this learning process) then reboot the system:

```
shutdown -r now
```

Note: upon reboot, if you use gpm, move the mouse on the terminal to ensure those accesses are logged otherwise there will be no rules for gpm. However, it is easy enough to generate those rules later. Also logging into each console that permits a user login will include those in the roles. Transition to su will also be a time saver. Since I limit root login to a single console, I perform that login as well. If you plan to connect and disconnect usb devices, do this also so the rules are added. I'm assuming no X at this point because I have been working with servers. [TODO: add X capability to the process.]

This creates a learning log in /gradm.initial.learning.log file that can be used to generate roles (which is another term for a policy statement).

To generate the intial policy statement:

```
#/sbin/gradm -F -L /gradm.initial.learning.log -O /gradm.initial.learning.roles
```

Now you have your first policy, so put it where it will be useful.

```
#cat /gradm.initial.learning.roles >> /etc/grsec/policy
```

and clean up

```
#rm /gradm.initial.learning*
```

Then set to autostart rather than autolearn:

```
#nano -w /etc/conf.d/local.start
```

then insert:

```
# /etc/conf.d/local.start

# This is a good place to load any misc programs

# on startup (use &>/dev/null to hide output)

#/sbin/gradm -F -L /gradm.initial.learning.log

/sbin/gradm -E

```

Now, test the workability of the system active:

```
#/sbin/gradm -E
```

and try the system in various ways.

Examine the logs.

If the system does not perform well, investigate the logs. Really, look at the logs anyway:

```
#/sbin/gradm -a admin

(enter password)

#grep grsec /var/log/kern.log | less

```

Now locate the activate line in the log, something like this:

Jan 29 13:59:45 localhost kernel: [  151.460617] grsec: (root:U:/sbin/gram) grsecurity 2.1.13 RBAC system load by /sbin/gram[gram:4729] ui/eui:0/0 gi/egi:0/0, parent /sbin/runscript.sh[runscript.sh:4728] ui/eui:0/0 gi/egi:0/0

and all the denied message following are your bogy to fix. Let's see how to fix those.

The logging in grsecurity RBAC is pretty verbose and the role used for a specific denial is in the message. For example, take these two denial messages:

Jan 30 04:21:01 localhost kernel: [51827.619562] grsec: (root:U:/usr/sbin/cron) denied access to hidden file /etc/crontab by /usr/sbin/cron[cron:4671] uid/euid:0/0 gid/egid:0/0, parent /sbin/init[init:1] uid/euid:0/0 gid/egid:0/0

Jan 30 04:21:48 localhost kernel: [51873.847865] grsec: (root:U:/) denied executable mmap of /bin/nano by /bin/nano[nano:7419] uid/euid:0/0 gid/egid:0/0, parent /bin/bash[bash:7413] uid/euid:0/0 gid/egid:0/0

The first was using the root role but the subject "/usr/sbin/cron" while the second rule is using the root role but the subject "/".

To fix the second, we have some choices. This particular error occured because I had not transitioned to the admin role, so access is not available until I do (i.e. the error is not with the policy but between the keyboard and the chair  :Wink:  )

To fix the first, we'll need to add a rule to the role root and the subject /usr/sbin/cron to allow read access to /etc/crontab:

role root uG

... additional subjects but no intervening role statements

subject /usr/sbin/cron o {

        /                               h

        /etc/localtime                  r

        /etc/cron.d                     r 

/etc/crontab					r

        /var/spool/cron/crontabs        r

        -CAP_ALL

        bind    disabled

        connect disabled

}

But also, looking at this list, we probably will fail hourly trying to run what is in /etc/cron.hourly, etc. So fixing that manually can be cumbersome. So lets try learning again, this time at the subject level by adding a lower case l to the subject:

role root uG

... additional subjects but no intervening role statements

subject /usr/sbin/cron ol {

        /                               h

        /etc/localtime                  r

        /etc/cron.d                     r 

        /etc/crontab					r

        /var/spool/cron/crontabs        r

        -CAP_ALL

        bind    disabled

        connect disabled

}

Note: I left the current rules in place. We'll need to merge them later because we may not encounter all situations in the current roles.

Then start the system for learning:

```
# /sbin/gradm -L /gradm.cron.learning.log 

```

Note: we omitted the -F because we are learning a specific item, not full system learning. If you do accidentally leave the -F in you get the cryptic message: 

Duplicate role :::kernel::: on line 788 of /etc/grsec/policy.

The RBAC system will not be allowed to be enabled until this error is fixed.

Now generate the rules (it is a capital o not zero):

```
# /sbin/gradm -L /gradm.cron.learning.log -O /gradm.cron.learning.roles

```

Merging the learned subject rules I recommend extracting the subject section of the learned roles and from the original roles and using diff to work with the differences. Diff rarely gives usable output from comparing the existing roles to the learned roles. I have a script (see below) to parse the roles and subjects into a directory structure with sorting. I created the script originally to help me understand the roles, but I find now that it helps with this merge process and really seeing the changes. The diff output from the directory structure is much cleaner. [TODO: complete testing on parser and post.]

Here are the generated roles:

###  THE BELOW SUBJECT(S) SHOULD BE ADDED TO THE USER ROLE "root" ###

subject / o {

	/				h

	-CAP_ALL

	bind	disabled

	connect	disabled

}

subject /usr/sbin/cron o {

user_transition_allow root

group_transition_allow root

	/				

	/bin				h

	/bin/bash			x

	/etc				r

	/etc/grsec			h

	/etc/ssh			h

	/etc/shadow-			h

	/etc/gshadow			h

	/etc/gshadow-			h

	/etc/ppp/chap-secrets		h

	/etc/ppp/pap-secrets		h

	/etc/samba/smbpasswd		h

	/lib				rx

	/proc				h

	/proc/sys/kernel/ngroups_max	r

	/usr				h

	/usr/sbin/cron			rx

	/usr/sbin/ssmtp			x

	/var				h

	/var/run			

	/var/spool/cron/crontabs	r

	/dev/grsec			h

	/dev/mem			h

	/dev/kmem			h

	/dev/port			h

	/dev/log			h

	/sys				h

	/boot				h

	-CAP_ALL

	+CAP_SETGID

	+CAP_SETUID

	bind	disabled

	connect	disabled

}

Then copy them to the appropriate place in the roles (where we did the learning in the first place) and remove the lower case l:

role root uG

... additional subjects but no intervening role statements

subject /usr/sbin/cron o {

user_transition_allow root

group_transition_allow root

        /                               

/boot				h

	/bin				h

	/bin/bash			x

	/dev/grsec			h

	/dev/mem			h

	/dev/kmem			h

	/dev/port			h

	/dev/log			h

	/etc				r

	/etc/grsec			h

	/etc/ssh			h

	/etc/shadow-			h

	/etc/gshadow			h

	/etc/gshadow-			h

	/etc/ppp/chap-secrets		h

	/etc/ppp/pap-secrets		h

	/etc/samba/smbpasswd		h

	/lib				rx

	/proc				h

	/proc/sys/kernel/ngroups_max	r

	/sys				h

	/usr				h

	/usr/sbin/cron			rx

	/usr/sbin/ssmtp			x

	/var				h

	/var/run

        /var/spool/cron/crontabs        r

        -CAP_ALL

+CAP_SETGID

	+CAP_SETUID

        bind    disabled

        connect disabled

}

Finalizing.

Once you have rebooted and confirmed the security is where you want it, finalize it by adding to /etc/sysctl.conf:

```

kernel.grsecurity.grsec_lock = 1

kernel.grsecurity.disable_modules = 1

```

Note: disabling module loading is not absolutely required. See the full grsecurity guide section 4 for other sysctl options to lock.

Now, optionally and for the super paranoid, I edit the policy which will look something like this giving admin the same as default as my decoy and my real admin role (in this case omni) gets the admin capability and don't forget to allow root to transition to your new admin role:

role omni sA

subject / rvka

	/ rwcdmlxi

role admin

subject / {

	/				h

	-CAP_ALL

	connect	disabled

	bind	disabled

}

role default

subject / {

	/				h

	-CAP_ALL

	connect	disabled

	bind	disabled

}

role root uG

role_transitions admin omni

role_allow_ip	0.0.0.0/32

subject /  {

	/				h

... lots of rules deleted

}

subject /usr/sbin/gpm o {

	/				h

	/dev				

	/dev/gpmctl			wd

	/dev/tty0			rw

	/dev/grsec			h

	/dev/mem			h

	/dev/kmem			h

	/dev/port			h

	/dev/log			h

	/var/run			

	/var/run/gpm.pid		wd

	-CAP_ALL

	+CAP_SYS_ADMIN

	+CAP_SYS_TTY_CONFIG

	bind	disabled

	connect	disabled

}

for reference, here is the policy I started with to make the above changes:

role admin sA

subject / rvka

	/ rwcdmlxi

role default

subject / {

	/				h

	-CAP_ALL

	connect	disabled

	bind	disabled

}

role root uG

role_transitions admin

role_allow_ip	0.0.0.0/32

subject /  {

	/				h

... lots of rules deleted

}

subject /usr/sbin/gpm o {

	/				h

	/dev				

	/dev/gpmctl			wd

	/dev/tty0			rw

	/dev/grsec			h

	/dev/mem			h

	/dev/kmem			h

	/dev/port			h

	/dev/log			h

	/var/run			

	/var/run/gpm.pid		wd

	-CAP_ALL

	+CAP_SYS_ADMIN

	+CAP_SYS_TTY_CONFIG

	bind	disabled

	connect	disabled

}

Here is my script to parse and compare rules.

It is rudimentary at this stage but by reviewing the results at the end, it is clear the differences between the policies.

In the current directory, save the existing policy as etc.grsec.policy and the new policy as etc.grsec.learned.policy.

The script creates a tree of existing rules in the existing/ directory and learned rules in the learned/ directory.

The "Only in" lines reflect full subject and role differences. The initial slash in paths of subjects are replaced by the word slash.

Rules are sorted to have better matching.

Here's the script:

```

#!/bin/bash

echo " ensure in the directory to save policy tree"

echo " press ctrl+c to exit "

read

wd="`pwd`"

echo " working directory ${wd}"

# verify learned policy file provided

learned="${wd}/etc.grsec.learned.policy"

# verify existing policy updated

existing="${wd}/etc.grsec.policy"

mkdir existing

mkdir learned

cd existing

#  verify updated file date matches last update of /etc/grsec/policy

for policyfile in ${existing} ${learned}

do

#policyfile=${existing}

# regenerate existing policy tree

mode=0

exec 3< ${policyfile}

while read aline <&3

do

echo " ${mode} processing line ${aline} "

case ${mode} in 

   0) 

      # test for role start

      # if [ `expr match "${v}" "role "` -eq 5 ] ; then echo "mess1" ; else echo "mess2"; fi

      if [ `expr match "${aline}" "role "` -eq 5 ] ; then

         # if role start found

         # set role name in role variable from statement

         role="`echo "${aline}" | cut -d" " -f 2`"

         echo " found role ${role} "

         # create role directory

         mkdir ${role}

         cd ${role}

         # create role file with role statement

         echo "${aline}" > role

         # set to role mode

         mode=role

      #else

         # skip record

      fi

#   break

   ;;

   role)

      # test for subject statements

      if [ `expr match "${aline}" "subject "` -eq 8 ] ; then

         # set subject to subject name

         subject="`echo "${aline}" | cut -d" " -f 2`"

         if [ "${subject}" == "/" ] ; then 

            subject="slash"

         else

            subject="`echo "slash${subject}" | tr "/" "."`"

         fi 

         echo " found subject ${subject} "

         # create subject file with subject statement

         echo "${aline}" > ${subject}

         # sort role file

         cat role | sort > role.sorted

         mv role.sorted role

         # set to subject mode

         mode=subject

      else

         if [ `expr match "${aline}" "role "` -eq 5 ] ; then

            # if role start found

            # move up dir

            cd ..

            # set role name in role variable from statement

            role="`echo "${aline}" | cut -d" " -f 2`"

            echo " found role ${role} "

            # create role directory

            mkdir ${role}

            cd ${role}

            # create role file with role statement

            echo "${aline}" >> role

            # set to role mode

            mode=role

         else

            # continue to write role statements

            echo "${aline}" >> role

         fi

      fi

#   break

   ;;

   subject)

      if [ `expr match "${aline}" "subject "` -eq 8 ] ; then

         # set subject to subject name

         subject="`echo "${aline}" | cut -d" " -f 2`"

         if [ "${subject}" == "/" ] ; then 

            subject="slash"

         else

            subject="`echo "slash${subject}" | tr "/" "."`"

         fi 

         echo " found subject ${subject} "

         # create subject file with subject statement

         echo "${aline}" > ${subject}

         # sort role file

         cat role | sort > role.sorted

         mv role.sorted role

         # set to subject mode

         mode=subject

      else

      # write subject to file

      echo "${aline}" >> ${subject}

      # test for end of subject

      if [ `expr match "${aline}" ".*}.*"` -gt 0 ] ; then

         # end of subject } found

         echo " found end of subject ${subject}"

         # set mode to role

         mode=role

         # sort subject files

         cat ${subject} | sort > ${subject}.sorted

         mv ${subject}.sorted ${subject}

      else

         if [ `expr match "${aline}" "role "` -eq 5 ] ; then

            # if role start found

            # move up dir

            cd ..

            # set role name in role variable from statement

            role="`echo "${aline}" | cut -d" " -f 2`"

            echo " found role ${role} "

            # create role directory

            mkdir ${role}

            cd ${role}

            # create role file with role statement

            echo "${aline}" >> role

            # set to role mode

            mode=role

         fi

      fi

      fi

#   break

   ;;

esac

done 

exec 3>&-

cd ${wd}/learned

# repeat for learned policy to verify changes

done

# show differences, omitting deletions from existing tree

cd ${wd}

echo "========begin showing differences============="

diff existing/ learned/ > result1

diff existing/ learned/ | grep Common | cut -d" " -f 3,5 | xargs -l diff -y -B >> result1

echo 'enter in less to review changes in context "/[<>\|]" '

echo '  then enter /Only to see the newly added sections '

read

less result1

```

Last edited by schmeggahead on Sat Feb 13, 2010 4:39 pm; edited 1 time in total

----------

## Sadako

No response to this yet?

I finally got a decent new machine, so I can make use of kvm (which runs perfectly under a fully hardened kernel, sweet), so I'll be playing around with grsec's RBAC within a vm, I'll use this as a starting point and get back to you with any feedback or issues.

Thanks.

Oh, when emerging stuff, do you disable RBAC completely?

----------

## schmeggahead

I just figured out how to do the start up and shutdown learning using local.start and local.stop

I'm changing the local.stop process to include /sbin/gradm -D < .file.with.your.password

once restarting the system, use /sbin/gradm -F -L /full.learning.log.name -O /full.policy.name

But I went to the grsecurity forum and posted a poll to see who uses auto start and how here: http://forums.grsecurity.net/viewtopic.php?f=5&t=2248

no luck on the hardened chat either.

----------

