Auditing a single SMF service

Sometimes there is a need to activate auditing for a single SMF service. The problem: There is no simple setting like “do_audit” or so for a service. But you could do this by using SMF itself.

In this article i assume some knowledge about the auditing subsystem, so you know the audit events that are logged for a processes are controlled by the process preselection mask. For example the bash on my system is auditing all exec calls (normal and with pfexec set) and events of the class lo:

# auditconfig -getpinfo 2194
audit id = jmoekamp(100)
process preselection mask = ex,lo(0x80001000,0x80001000)
terminal id (maj,min,host) = 14392,136704,unknown(192.168.0.18)
audit session id = 4132001775

Okay, at first we need a script to set the audit flags for the processes of a service. Just a warning: All the scripts and manifests are just quick and dirty “proof of concept”-scripts. They may break immediately. They may do harm. There may be a security hole as gapping as a large barn door. And don’t ask me for support. But they should give you an idea what you have to do.

At first we need to find out what processes need a new preselection mask. I hacked something along the lines of this: Essentially it looks for the contract id of a service and then finds all processes of the contract. Afterwards it iterates through the process ids by the use of xargs and sets the new pmask to ex. I will post something that is significantly more elegant than this soon (as this script is elegant as a brick). But i need this article fast … well … out of reasons. I have decided to use the hal service in this example for no reason besides the fact that it has multiple processes. I put this file in /lib/svc/method and made it executable afterwards. The service i want to audit is defined in the variable service. In this example it’s set to svc:/system/hal:default:

root@solaris:/lib/svc/method# cat custom_auditsetter_hal.sh 
#!/bin/ksh
. /lib/svc/share/smf_include.sh

echo "warning - unsupported quick and dirty hack in use"

function setauditflags {
 service="svc:/system/hal:default"
 contract_of_service=`svcs -l $service | grep "contract_id"  | awk '{print $2}'`
 pids_of_contract=`pgrep -c $contract_of_service`
 echo "$pids_of_contract" | xargs -I {} -t auditconfig -setpmask {}  ex
 exit $SMF_EXIT_OK
}

case "$1" in
'start')
        setauditflags 
        sleep 2
        ;;
'restart')
        setauditflags   
        sleep 2
        ;;
'stop')
        ;;
*)
        echo "Usage: $0 { start | stop }"
        exit 1
        ;;
esac

exit $SMF_EXIT_OK

We talk about SMF. So we need a SMF manifest next. I created the skeleton of it for Solaris 11.3 with svcbundle and customized it. In principle it should work for Solaris 10 as well. I put this file into the homedirectory of root and named the file customauditsetterhal.xml.

<?xml version="1.0" ?>
<!DOCTYPE service_bundle
  SYSTEM '/usr/share/lib/xml/dtd/service_bundle.dtd.1'>
<service_bundle type="manifest" name="site/custom_auditsetter_hal">
    <service version="1" type="service" name="site/custom_auditsetter_hal">
        <dependency restart_on="none" type="service"
            name="multi_user_dependency" grouping="require_all">
            <service_fmri value="svc:/milestone/multi-user"/>
        </dependency>
 <font color="red">       &lt;dependency restart_on="refresh" type="service"
            name="auditsetter_dependency" grouping="require_all"&gt;
            &lt;service_fmri value="svc:/system/hal:default"/&gt;
        &lt;/dependency&gt;</font>
        &lt;exec_method timeout_seconds="60" type="method" name="start"
            exec="/lib/svc/method/custom_auditsetter_hal.sh start"/&gt;
        &lt;exec_method timeout_seconds="60" type="method" name="stop"
            exec=":true"/&gt;
        &lt;exec_method timeout_seconds="60" type="method" name="refresh"
            exec=":true"/&gt;
        &lt;property_group type="framework" name="startd"&gt;
            &lt;propval type="astring" name="duration" value="transient"/&gt;
        &lt;/property_group&gt;
        &lt;instance enabled="false" name="default"/&gt;
        &lt;template&gt;
            &lt;common_name&gt;
                &lt;loctext xml:lang="C"&gt;Custom AuditSetter for HAL daemon &lt;/loctext&gt;
            &lt;/common_name&gt;
            &lt;description&gt;
                &lt;loctext xml:lang="C"&gt;
                Custom Audit Setter for HAL daemon 
                &lt;/loctext&gt;
            &lt;/description&gt;
        &lt;/template&gt;
    &lt;/service&gt;
&lt;/service_bundle

The important part is the red one. I manually added it to the auto generated manifest. It defines a dependency to the service which should be audited. So after the service we want to audit is started, this service will start and set the process preselection mask. With the restart_on we further configured that this service should restart and set the mask even if the service is just refreshed as well when it’s restarted or disabled/enabled.

Okay. Now we have to do the necessary steps to use it:

# cp customauditsetterhal.xml /lib/svc/manifest/site
# svcadm restart manifest-import

Before we enable the service, let us check the current state of the preselection mask:

# pgrep hal | xargs -I {} auditconfig -getpinfo {} | grep "preselection"
process preselection mask = no(0x0,0x0)
process preselection mask = no(0x0,0x0)
process preselection mask = no(0x0,0x0)
process preselection mask = no(0x0,0x0)
process preselection mask = no(0x0,0x0)

Now we will enable the new service:

# svcadm enable svc:/site/custom_auditsetter_hal:default

Next step is to check the state of the new service

# svcs "*hal*"
STATE          STIME    FMRI
online         19:14:15 svc:/system/hal:default
online         19:53:53 svc:/site/custom_auditsetter_hal:default

The dependencies are according to our configuration. The new service is dependant on svc:/milestone/multi-user:default and svc:/system/hal:default.

# svcs -d svc:/site/custom_auditsetter_hal
STATE          STIME    FMRI
online         20:25:00 svc:/milestone/multi-user:default
online         20:25:01 svc:/system/hal:default

And now we can look if the service did its job:

# pgrep hal | xargs -I {} auditconfig -getpinfo {} | grep "preselection"
process preselection mask = ex(0x80000000,0x80000000)
process preselection mask = ex(0x80000000,0x80000000)
process preselection mask = ex(0x80000000,0x80000000)
process preselection mask = ex(0x80000000,0x80000000)
process preselection mask = ex(0x80000000,0x80000000)

Okay, let’s restart the service.

# svcadm restart hal

It will automatically execute the commands to set the new process preselection mask as indicated by the logfile of the SMF service to the new processes created by the restart

# svcs -Lv svc:/site/custom_auditsetter_hal
[ Jun  2 20:13:44 Stopping because dependency activity requires stop. ]
[ Jun  2 20:13:44 Executing stop method (:true). ]
[ Jun  2 20:13:44 Executing start method ("/lib/svc/method/custom_auditsetter_hal.sh start"). ]
auditconfig -setpmask 2911 ex
auditconfig -setpmask 2917 ex
auditconfig -setpmask 2913 ex
auditconfig -setpmask 2910 ex
auditconfig -setpmask 2914 ex
[ Jun  2 20:13:44 Method "start" exited with status 0. ]

After a reboot the command will be executed as well:

# svcs -Lv svc:/site/custom_auditsetter_hal
[ Jun  2 20:13:44 Method "start" exited with status 0. ]
[ Jun  2 20:15:42 Executing start method ("/lib/svc/method/custom_auditsetter_hal.sh start"). ]
auditconfig -setpmask 564 ex
auditconfig -setpmask 541 ex
auditconfig -setpmask 545 ex
auditconfig -setpmask 542 ex
auditconfig -setpmask 544 ex
[ Jun  2 20:15:44 Method "start" exited with status 0. ]

Okay, of course a simple property would be nicer but with SMF dependencies you can work around this.