kde-apps/ksirk: x86 stable (bug #661810)
[gentoo.git] / eclass / fcaps.eclass
1 # Copyright 1999-2015 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3
4 # @ECLASS: fcaps.eclass
5 # @MAINTAINER:
6 # base-system@gentoo.org
7 # @BLURB: function to set POSIX file-based capabilities
8 # @DESCRIPTION:
9 # This eclass provides a function to set file-based capabilities on binaries.
10 # This is not the same as USE=caps which controls runtime capability changes,
11 # often via packages like libcap.
12 #
13 # Due to probable capability-loss on moving or copying, this happens in
14 # pkg_postinst phase (at least for now).
15 #
16 # @EXAMPLE:
17 # You can manually set the caps on ping and ping6 by doing:
18 # @CODE
19 # pkg_postinst() {
20 #       fcaps cap_net_raw bin/ping bin/ping6
21 # }
22 # @CODE
23 #
24 # Or set it via the global ebuild var FILECAPS:
25 # @CODE
26 # FILECAPS=(
27 #       cap_net_raw bin/ping bin/ping6
28 # )
29 # @CODE
30
31 if [[ -z ${_FCAPS_ECLASS} ]]; then
32 _FCAPS_ECLASS=1
33
34 IUSE="+filecaps"
35
36 # We can't use libcap-ng atm due to #471414.
37 DEPEND="filecaps? ( sys-libs/libcap )"
38
39 # @ECLASS-VARIABLE: FILECAPS
40 # @DEFAULT_UNSET
41 # @DESCRIPTION:
42 # An array of fcap arguments to use to automatically execute fcaps.  See that
43 # function for more details.
44 #
45 # All args are consumed until the '--' marker is found.  So if you have:
46 # @CODE
47 #       FILECAPS=( moo cow -- fat cat -- chubby penguin )
48 # @CODE
49 #
50 # This will end up executing:
51 # @CODE
52 #       fcaps moo cow
53 #       fcaps fat cat
54 #       fcaps chubby penguin
55 # @CODE
56 #
57 # Note: If you override pkg_postinst, you must call fcaps_pkg_postinst yourself.
58
59 # @FUNCTION: fcaps
60 # @USAGE: [-o <owner>] [-g <group>] [-m <mode>] [-M <caps mode>] <capabilities> <file[s]>
61 # @DESCRIPTION:
62 # Sets the specified capabilities on the specified files.
63 #
64 # The caps option takes the form as expected by the cap_from_text(3) man page.
65 # If no action is specified, then "=ep" will be used as a default.
66 #
67 # If the file is a relative path (e.g. bin/foo rather than /bin/foo), then the
68 # appropriate path var ($D/$ROOT/etc...) will be prefixed based on the current
69 # ebuild phase.
70 #
71 # The caps mode (default 711) is used to set the permission on the file if
72 # capabilities were properly set on the file.
73 #
74 # If the system is unable to set capabilities, it will use the specified user,
75 # group, and mode (presumably to make the binary set*id).  The defaults there
76 # are root:0 and 4711.  Otherwise, the ownership and permissions will be
77 # unchanged.
78 fcaps() {
79         debug-print-function ${FUNCNAME} "$@"
80
81         # Process the user options first.
82         local owner='root'
83         local group='0'
84         local mode='4711'
85         local caps_mode='711'
86
87         while [[ $# -gt 0 ]] ; do
88                 case $1 in
89                 -o) owner=$2; shift;;
90                 -g) group=$2; shift;;
91                 -m) mode=$2; shift;;
92                 -M) caps_mode=$2; shift;;
93                 *) break;;
94                 esac
95                 shift
96         done
97
98         [[ $# -lt 2 ]] && die "${FUNCNAME}: wrong arg count"
99
100         local caps=$1
101         [[ ${caps} == *[-=+]* ]] || caps+="=ep"
102         shift
103
104         local root
105         case ${EBUILD_PHASE} in
106         compile|install|preinst)
107                 root=${ED:-${D}}
108                 ;;
109         postinst)
110                 root=${EROOT:-${ROOT}}
111                 ;;
112         esac
113         root=${root%/}
114
115         # Process every file!
116         local file
117         for file ; do
118                 [[ ${file} != /* ]] && file="${root}/${file}"
119
120                 if use filecaps ; then
121                         # Try to set capabilities.  Ignore errors when the
122                         # fs doesn't support it, but abort on all others.
123                         debug-print "${FUNCNAME}: setting caps '${caps}' on '${file}'"
124
125                         # If everything goes well, we don't want the file to be readable
126                         # by people.
127                         chmod ${caps_mode} "${file}" || die
128
129                         # Set/verify funcs for sys-libs/libcap.
130                         _libcap()        { setcap "${caps}" "${file}" ; }
131                         _libcap_verify() { setcap -v "${caps}" "${file}" >/dev/null ; }
132
133                         # Set/verify funcs for sys-libs/libcap-ng.
134                         # Note: filecap only supports =ep mode.
135                         # It also expects a different form:
136                         #  setcap cap_foo,cap_bar
137                         #  filecap foo bar
138                         _libcap_ng() {
139                                 local caps=",${caps%=ep}"
140                                 filecap "${file}" "${caps//,cap_}"
141                         }
142                         _libcap_ng_verify() {
143                                 # libcap-ng has a crappy interface
144                                 local rcaps icaps caps=",${caps%=ep}"
145                                 rcaps=$(filecap "${file}" | \
146                                         sed -nr \
147                                                 -e "s:^.{${#file}} +::" \
148                                                 -e 's:, +:\n:g' \
149                                                 -e 2p | \
150                                         LC_ALL=C sort)
151                                 [[ ${PIPESTATUS[0]} -eq 0 ]] || return 1
152                                 icaps=$(echo "${caps//,cap_}" | LC_ALL=C sort)
153                                 [[ ${rcaps} == ${icaps} ]]
154                         }
155
156                         local out cmd notfound=0
157                         for cmd in _libcap _libcap_ng ; do
158                                 if ! out=$(LC_ALL=C ${cmd} 2>&1) ; then
159                                         case ${out} in
160                                         *"command not found"*)
161                                                 : $(( ++notfound ))
162                                                 continue
163                                                 ;;
164                                         # ENOTSUP and EOPNOTSUPP might be the same value which means
165                                         # strerror() on them is unstable -- we can get both. #559608
166                                         *"Not supported"*|\
167                                         *"Operation not supported"*)
168                                                 local fstype=$(stat -f -c %T "${file}")
169                                                 ewarn "Could not set caps on '${file}' due to missing filesystem support:"
170                                                 ewarn "* enable XATTR support for '${fstype}' in your kernel (if configurable)"
171                                                 ewarn "* mount the fs with the user_xattr option (if not the default)"
172                                                 ewarn "* enable the relevant FS_SECURITY option (if configurable)"
173                                                 break
174                                                 ;;
175                                         *)
176                                                 eerror "Setting caps '${caps}' on file '${file}' failed:"
177                                                 eerror "${out}"
178                                                 die "could not set caps"
179                                                 ;;
180                                         esac
181                                 else
182                                         # Sanity check that everything took.
183                                         ${cmd}_verify || die "Checking caps '${caps}' on '${file}' failed"
184
185                                         # Everything worked.  Move on to the next file.
186                                         continue 2
187                                 fi
188                         done
189                         if [[ ${notfound} -eq 2 ]] && [[ -z ${_FCAPS_WARNED} ]] ; then
190                                 _FCAPS_WARNED="true"
191                                 ewarn "Could not find cap utils; make sure libcap or libcap-ng is available."
192                         fi
193                 fi
194
195                 # If we're still here, setcaps failed.
196                 debug-print "${FUNCNAME}: setting owner/mode on '${file}'"
197                 chown "${owner}:${group}" "${file}" || die
198                 chmod ${mode} "${file}" || die
199         done
200 }
201
202 # @FUNCTION: fcaps_pkg_postinst
203 # @DESCRIPTION:
204 # Process the FILECAPS array.
205 fcaps_pkg_postinst() {
206         local arg args=()
207         for arg in "${FILECAPS[@]}" "--" ; do
208                 if [[ ${arg} == "--" ]] ; then
209                         fcaps "${args[@]}"
210                         args=()
211                 else
212                         args+=( "${arg}" )
213                 fi
214         done
215 }
216
217 EXPORT_FUNCTIONS pkg_postinst
218
219 fi