net-irc/limnoria: use HTTPS for GitHub and HOMEPAGE
[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
114         # Process every file!
115         local file
116         for file ; do
117                 [[ ${file} != /* ]] && file="${root}${file}"
118
119                 if use filecaps ; then
120                         # Try to set capabilities.  Ignore errors when the
121                         # fs doesn't support it, but abort on all others.
122                         debug-print "${FUNCNAME}: setting caps '${caps}' on '${file}'"
123
124                         # If everything goes well, we don't want the file to be readable
125                         # by people.
126                         chmod ${caps_mode} "${file}" || die
127
128                         # Set/verify funcs for sys-libs/libcap.
129                         _libcap()        { setcap "${caps}" "${file}" ; }
130                         _libcap_verify() { setcap -v "${caps}" "${file}" >/dev/null ; }
131
132                         # Set/verify funcs for sys-libs/libcap-ng.
133                         # Note: filecap only supports =ep mode.
134                         # It also expects a different form:
135                         #  setcap cap_foo,cap_bar
136                         #  filecap foo bar
137                         _libcap_ng() {
138                                 local caps=",${caps%=ep}"
139                                 filecap "${file}" "${caps//,cap_}"
140                         }
141                         _libcap_ng_verify() {
142                                 # libcap-ng has a crappy interface
143                                 local rcaps icaps caps=",${caps%=ep}"
144                                 rcaps=$(filecap "${file}" | \
145                                         sed -nr \
146                                                 -e "s:^.{${#file}} +::" \
147                                                 -e 's:, +:\n:g' \
148                                                 -e 2p | \
149                                         LC_ALL=C sort)
150                                 [[ ${PIPESTATUS[0]} -eq 0 ]] || return 1
151                                 icaps=$(echo "${caps//,cap_}" | LC_ALL=C sort)
152                                 [[ ${rcaps} == ${icaps} ]]
153                         }
154
155                         local out cmd notfound=0
156                         for cmd in _libcap _libcap_ng ; do
157                                 if ! out=$(LC_ALL=C ${cmd} 2>&1) ; then
158                                         case ${out} in
159                                         *"command not found"*)
160                                                 : $(( ++notfound ))
161                                                 continue
162                                                 ;;
163                                         # ENOTSUP and EOPNOTSUPP might be the same value which means
164                                         # strerror() on them is unstable -- we can get both. #559608
165                                         *"Not supported"*|\
166                                         *"Operation not supported"*)
167                                                 local fstype=$(stat -f -c %T "${file}")
168                                                 ewarn "Could not set caps on '${file}' due to missing filesystem support:"
169                                                 ewarn "* enable XATTR support for '${fstype}' in your kernel (if configurable)"
170                                                 ewarn "* mount the fs with the user_xattr option (if not the default)"
171                                                 ewarn "* enable the relevant FS_SECURITY option (if configurable)"
172                                                 break
173                                                 ;;
174                                         *)
175                                                 eerror "Setting caps '${caps}' on file '${file}' failed:"
176                                                 eerror "${out}"
177                                                 die "could not set caps"
178                                                 ;;
179                                         esac
180                                 else
181                                         # Sanity check that everything took.
182                                         ${cmd}_verify || die "Checking caps '${caps}' on '${file}' failed"
183
184                                         # Everything worked.  Move on to the next file.
185                                         continue 2
186                                 fi
187                         done
188                         if [[ ${notfound} -eq 2 ]] && [[ -z ${_FCAPS_WARNED} ]] ; then
189                                 _FCAPS_WARNED="true"
190                                 ewarn "Could not find cap utils; make sure libcap or libcap-ng is available."
191                         fi
192                 fi
193
194                 # If we're still here, setcaps failed.
195                 debug-print "${FUNCNAME}: setting owner/mode on '${file}'"
196                 chown "${owner}:${group}" "${file}" || die
197                 chmod ${mode} "${file}" || die
198         done
199 }
200
201 # @FUNCTION: fcaps_pkg_postinst
202 # @DESCRIPTION:
203 # Process the FILECAPS array.
204 fcaps_pkg_postinst() {
205         local arg args=()
206         for arg in "${FILECAPS[@]}" "--" ; do
207                 if [[ ${arg} == "--" ]] ; then
208                         fcaps "${args[@]}"
209                         args=()
210                 else
211                         args+=( "${arg}" )
212                 fi
213         done
214 }
215
216 EXPORT_FUNCTIONS pkg_postinst
217
218 fi