#!/bin/bash
# vim:expandtab:tabstop=4
#
# author:    chris friedhoff - chris@friedhoff.org
# version:   pcaps4suid0  3  Tue Mar 11 2008
#
# Configfile support added by Menno Duursma <druiloor@zonnet.nl>
#
# changelog:
# 1 - initial release suid02pcaps
# 2 - renamend to pcaps4suid0
#      implement idea of change between permitted/effective set
#      or iherited/effective set (pam_cap.so)
# 3 - changed 'attr -S -r' to 'setcap -r' and removed attr code
#
#
#
# change different suid-0 binaries away from suid-0 to using
# POSIX Capabilities through their Permitted and Effective Set
# --> legacy support
# --> use SET=pe
#
# 
# OR change different suid-0 binaries away from suid-0 to using
# POSIX Capabilities through their Inherited and Effective Set
# --> PAM support to set Inheritance set through pam_cap.so
# --> use SET=ie
#
# 
#
#

#set -vx

PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/libexec

CONFIGFILE=/etc/default/capabilities

# we put it into this set
#########################
SET=pe
#SET=ie



##FROM HERE ONLY LOGIC
######################

p4s_test(){
    # are we sane?
    WICH=`which which 2>/dev/null`
    if [ $WICH == "" ]; then
        # thats bad
        echo "Sorry, I haven't found which"
        exit
    fi

    if [ ! -r $CONFIGFILE ]; then
        echo "Error: cannot read $CONFIGFILE" >&2
        exit
    fi

    for util in grep cut tr; do
        if ! command -v $util &>/dev/null; then
            echo "Error: command -v $util" >&2
            exit
        fi
    done

    APPS=$( while read line; do \
        echo "$line" | \
            grep -v -e ^# -e ^$ \
                | cut -d= -f1,2 \
                    | tr '\n' " " ; done \
        < $CONFIGFILE )

    # this apps were converted/reverted
    APPSARRAY=( $APPS )

    if [ -z "$APPSARRAY" ]; then
        echo "Sorry, no app(s) to convert/revert" >&2
        exit
    fi

    # we needt his apps
    CHMOD=`which chmod 2>/dev/null`
    SETCAP=`which setcap 2>/dev/null`
    if [ "$CHMOD" == "" -o "$SETCAP" == "" ]; then
        echo "Sorry, I'm missing chmod or setcap !"
        exit
    fi

    # checking setcap for SET_SETFCAP PCap ?
    # for now we stick to root
    #if [ "$( id -u )" != "0" ]; then
    #    echo "Sorry, you must be root !"
    #    exit 1
    #fi
}



p4s_app_convert(){
    # convert a single app

    # $1 is appname=POSIXCaps
    local appname=`echo $1 | cut -d= -f1`
    local POSIXCaps=`echo $1 | cut -d= -f2`

    # well symlinks to apps, so we use -a ...
    APP=`which -a $appname 2>/dev/null`
    if [ "$APP" != "" ]; then
        FOUND=no
        for i in $APP; do
            # ... and are looking for symlinks
            if [ -f "$i" -a ! -L $i -a "$FOUND"=="no" ]; then
                echo "converting $i $SET=$POSIXCaps"
                if ! chmod u-s $i; then
                    echo "Error: chmod u-s $i" >&2
                    break
                fi
                if ! setcap $POSIXCaps=$SET $i; then
                    echo "Error: setcap $POSIXCaps=$SET $i" >&2
                    break
                fi
                FOUND=yes
            fi
        done
        if [ "$FOUND" == "no" ]; then
            # 'which' found only symlinks
            echo "1 haven't found $appname"
        fi
    else
        # 'which' hasn't anything given back
        echo "haven't found $appname"
    fi
}



p4s_app_revert(){
    # revert a singel app

    # $1 is appname=POSIXCaps
    local appname=`echo $1 | cut -d= -f1`

    APP=`which -a $appname 2>/dev/null`
    if [ "$APP" != "" ]; then
        FOUND=no
        for i in $APP; do
            if [ -f "$i" -a ! -L $i -a "$FOUND"=="no" ]; then
                echo "reverting $i" 
                if ! chmod u+s $i; then
                    echo "Error: chmod u+s $i" >&2
                fi
                if ! setcap -r $i 2>/dev/null; then
                    echo "Error: setcap -r $i 2>/dev/null" >&2
                    break
                fi 
                FOUND=yes
            fi
        done
        if [ "$FOUND" == "no" ]; then
            echo "1 haven't found $appname"
        fi
    else
        echo "haven't found $appname"
    fi
}



p4s_app_list(){
    # list a singel app

    # $1 is appname=POSIXCaps
    local appname=`echo $1 | cut -d= -f1`

    APP=`which -a $appname 2>/dev/null`
    if [ "$APP" != "" ]; then
        FOUND=no
        for i in $APP; do
            if [ -f "$i" -a ! -L $i -a "$FOUND"=="no" ]; then
                # Pretty print
                echo "$i = $( ls -l $i \
                                 | cut -d' ' -f1,3-4 \
                                     | tr '\n' " " 2>/dev/null \
                                         && getcap -v $i \
                                             | cut -d= -f2 2>/dev/null )"
                FOUND=yes
            fi
        done
        if [ "$FOUND" == "no" ]; then
            echo "1 haven't found $appname"
        fi
    else
        echo "haven't found $appname"
    fi
}



p4s_convert(){
    # we go throug the APPSARRAY and call s2p_app_convert to do the job
    COUNTER=0
    let UPPER=${#APPSARRAY[*]}
    until [ $COUNTER == $UPPER ]; do
        p4s_app_convert ${APPSARRAY[$COUNTER]} ${!APPSARRAY[$COUNTER]}
        let COUNTER+=1
    done
}



p4s_revert(){
    COUNTER=0
    let UPPER=${#APPSARRAY[*]}
    until [ $COUNTER == $UPPER ]; do
        p4s_app_revert ${APPSARRAY[$COUNTER]}
        let COUNTER+=1
    done

}


p4s_list(){
    COUNTER=0
    let UPPER=${#APPSARRAY[*]}
    until [ $COUNTER == $UPPER ]; do
        p4s_app_list ${APPSARRAY[$COUNTER]}
        let COUNTER+=1
    done

}

p4s_usage(){
    echo "Usage: capabilities [con(vert)|rev(ert)|list|help]"
    echo
    echo "         con|convert - from setuid0 to POSIX Capabilities"
    echo "         rev|revert  - from POSIX Capabilities back to setuid0"
    echo "         ls|list     - print object capabilities and setuid0 status"
    echo "         help        - this help message"
    echo
}



case "$1" in
    con|convert)
        p4s_test
        p4s_convert
        exit 0
        ;;
    rev|revert)
        p4s_test
        p4s_revert
        exit 0
        ;;
    ls|list)
        p4s_test
        p4s_list
        exit 0
        ;;
    help)
        p4s_usage
        exit 0
        ;;
    *)
        echo "Try 'capabilities help' for more information"
        exit 1
        ;;
esac
