1{ stdenv, lib, fetchFromGitLab, fetchpatch, makeWrapper, autoreconfHook
2, pkg-config, which
3, flex, bison
4, linuxHeaders ? stdenv.cc.libc.linuxHeaders
5, gawk
6, withPerl ? stdenv.hostPlatform == stdenv.buildPlatform && lib.meta.availableOn stdenv.hostPlatform perl, perl
7, withPython ? stdenv.hostPlatform == stdenv.buildPlatform && lib.meta.availableOn stdenv.hostPlatform python3, python3
8, swig
9, ncurses
10, pam
11, libnotify
12, buildPackages
13, coreutils
14, bash
15, gnugrep
16, gnused
17, kmod
18, writeShellScript
19, closureInfo
20, runCommand
21, libxcrypt
22}:
23
24let
25 apparmor-version = "3.1.2";
26
27 apparmor-meta = component: with lib; {
28 homepage = "https://apparmor.net/";
29 description = "A mandatory access control system - ${component}";
30 license = licenses.gpl2;
31 maintainers = with maintainers; [ julm thoughtpolice ];
32 platforms = platforms.linux;
33 };
34
35 apparmor-sources = fetchFromGitLab {
36 owner = "apparmor";
37 repo = "apparmor";
38 rev = "v${apparmor-version}";
39 hash = "sha256-0csF6dPel1CxbvNkg7fIrdPpnCM+hqht2a5nwPlR58A=";
40 };
41
42 aa-teardown = writeShellScript "aa-teardown" ''
43 PATH="${lib.makeBinPath [coreutils gnused gnugrep]}:$PATH"
44 . ${apparmor-parser}/lib/apparmor/rc.apparmor.functions
45 remove_profiles
46 '';
47
48 prePatchCommon = ''
49 chmod a+x ./common/list_capabilities.sh ./common/list_af_names.sh
50 patchShebangs ./common/list_capabilities.sh ./common/list_af_names.sh
51 substituteInPlace ./common/Make.rules \
52 --replace "/usr/bin/pod2man" "${buildPackages.perl}/bin/pod2man" \
53 --replace "/usr/bin/pod2html" "${buildPackages.perl}/bin/pod2html" \
54 --replace "/usr/share/man" "share/man"
55 substituteInPlace ./utils/Makefile \
56 --replace "/usr/include/linux/capability.h" "${linuxHeaders}/include/linux/capability.h"
57 '';
58
59 patches = lib.optionals stdenv.hostPlatform.isMusl [
60 (fetchpatch {
61 url = "https://git.alpinelinux.org/aports/plain/testing/apparmor/0003-Added-missing-typedef-definitions-on-parser.patch?id=74b8427cc21f04e32030d047ae92caa618105b53";
62 name = "0003-Added-missing-typedef-definitions-on-parser.patch";
63 sha256 = "0yyaqz8jlmn1bm37arggprqz0njb4lhjni2d9c8qfqj0kll0bam0";
64 })
65 ];
66
67 python = python3.withPackages (ps: with ps; [ setuptools ]);
68
69 # Set to `true` after the next FIXME gets fixed or this gets some
70 # common derivation infra. Too much copy-paste to fix one by one.
71 doCheck = false;
72
73 # FIXME: convert these to a single multiple-outputs package?
74
75 libapparmor = stdenv.mkDerivation {
76 pname = "libapparmor";
77 version = apparmor-version;
78
79 src = apparmor-sources;
80
81 # checking whether python bindings are enabled... yes
82 # checking for python3... no
83 # configure: error: python is required when enabling python bindings
84 strictDeps = false;
85
86 nativeBuildInputs = [
87 autoreconfHook
88 bison
89 flex
90 pkg-config
91 swig
92 ncurses
93 which
94 perl
95 ] ++ lib.optional withPython python;
96
97 buildInputs = [ libxcrypt ]
98 ++ lib.optional withPerl perl
99 ++ lib.optional withPython python;
100
101 # required to build apparmor-parser
102 dontDisableStatic = true;
103
104 prePatch = prePatchCommon + ''
105 substituteInPlace ./libraries/libapparmor/swig/perl/Makefile.am --replace install_vendor install_site
106 '';
107 inherit patches;
108
109 postPatch = ''
110 cd ./libraries/libapparmor
111 '';
112
113 # https://gitlab.com/apparmor/apparmor/issues/1
114 configureFlags = [
115 (lib.withFeature withPerl "perl")
116 (lib.withFeature withPython "python")
117 ];
118
119 outputs = [ "out" ] ++ lib.optional withPython "python";
120
121 postInstall = lib.optionalString withPython ''
122 mkdir -p $python/lib
123 mv $out/lib/python* $python/lib/
124 '';
125
126 inherit doCheck;
127
128 meta = apparmor-meta "library";
129 };
130
131 apparmor-utils = stdenv.mkDerivation {
132 pname = "apparmor-utils";
133 version = apparmor-version;
134
135 src = apparmor-sources;
136
137 strictDeps = true;
138
139 nativeBuildInputs = [ makeWrapper which python ];
140
141 buildInputs = [
142 bash
143 perl
144 python
145 libapparmor
146 libapparmor.python
147 ];
148
149 prePatch = prePatchCommon +
150 # Do not build vim file
151 lib.optionalString stdenv.hostPlatform.isMusl ''
152 sed -i ./utils/Makefile -e "/\<vim\>/d"
153 '' + ''
154 for file in utils/apparmor/easyprof.py utils/apparmor/aa.py utils/logprof.conf; do
155 substituteInPlace $file --replace "/sbin/apparmor_parser" "${apparmor-parser}/bin/apparmor_parser"
156 done
157 '';
158 inherit patches;
159 postPatch = "cd ./utils";
160 makeFlags = [ "LANGS=" ];
161 installFlags = [ "DESTDIR=$(out)" "BINDIR=$(out)/bin" "VIM_INSTALL_PATH=$(out)/share" "PYPREFIX=" ];
162
163 postInstall = ''
164 sed -i $out/bin/aa-unconfined -e "/my_env\['PATH'\]/d"
165 for prog in aa-audit aa-autodep aa-cleanprof aa-complain aa-disable aa-enforce aa-genprof aa-logprof aa-mergeprof aa-unconfined ; do
166 wrapProgram $out/bin/$prog --prefix PYTHONPATH : "$out/lib/${python.sitePackages}:$PYTHONPATH"
167 done
168
169 substituteInPlace $out/bin/aa-notify \
170 --replace /usr/bin/notify-send ${libnotify}/bin/notify-send \
171 --replace /usr/bin/perl "${perl}/bin/perl -I ${libapparmor}/${perl.libPrefix}"
172
173 substituteInPlace $out/bin/aa-remove-unknown \
174 --replace "/lib/apparmor/rc.apparmor.functions" "${apparmor-parser}/lib/apparmor/rc.apparmor.functions"
175 wrapProgram $out/bin/aa-remove-unknown \
176 --prefix PATH : ${lib.makeBinPath [ gawk ]}
177
178 ln -s ${aa-teardown} $out/bin/aa-teardown
179 '';
180
181 inherit doCheck;
182
183 meta = apparmor-meta "user-land utilities" // {
184 broken = !(withPython && withPerl);
185 };
186 };
187
188 apparmor-bin-utils = stdenv.mkDerivation {
189 pname = "apparmor-bin-utils";
190 version = apparmor-version;
191
192 src = apparmor-sources;
193
194 nativeBuildInputs = [
195 pkg-config
196 libapparmor
197 which
198 ];
199
200 buildInputs = [
201 libapparmor
202 ];
203
204 prePatch = prePatchCommon;
205 postPatch = ''
206 cd ./binutils
207 '';
208 makeFlags = [ "LANGS=" "USE_SYSTEM=1" ];
209 installFlags = [ "DESTDIR=$(out)" "BINDIR=$(out)/bin" "SBINDIR=$(out)/bin" ];
210
211 inherit doCheck;
212
213 meta = apparmor-meta "binary user-land utilities";
214 };
215
216 apparmor-parser = stdenv.mkDerivation {
217 name = "apparmor-parser";
218 version = apparmor-version;
219
220 src = apparmor-sources;
221
222 nativeBuildInputs = [ bison flex which ];
223
224 buildInputs = [ libapparmor ];
225
226 prePatch = prePatchCommon + ''
227 ## techdoc.pdf still doesn't build ...
228 substituteInPlace ./parser/Makefile \
229 --replace "/usr/bin/bison" "${bison}/bin/bison" \
230 --replace "/usr/bin/flex" "${flex}/bin/flex" \
231 --replace "/usr/include/linux/capability.h" "${linuxHeaders}/include/linux/capability.h" \
232 --replace "manpages htmlmanpages pdf" "manpages htmlmanpages"
233 substituteInPlace parser/rc.apparmor.functions \
234 --replace "/sbin/apparmor_parser" "$out/bin/apparmor_parser"
235 sed -i parser/rc.apparmor.functions -e '2i . ${./fix-rc.apparmor.functions.sh}'
236 '';
237 inherit patches;
238 postPatch = ''
239 cd ./parser
240 '';
241 makeFlags = [
242 "LANGS=" "USE_SYSTEM=1" "INCLUDEDIR=${libapparmor}/include"
243 "AR=${stdenv.cc.bintools.targetPrefix}ar"
244 ];
245 installFlags = [ "DESTDIR=$(out)" "DISTRO=unknown" ];
246
247 inherit doCheck;
248
249 meta = apparmor-meta "rule parser";
250 };
251
252 apparmor-pam = stdenv.mkDerivation {
253 pname = "apparmor-pam";
254 version = apparmor-version;
255
256 src = apparmor-sources;
257
258 nativeBuildInputs = [ pkg-config which ];
259
260 buildInputs = [ libapparmor pam ];
261
262 postPatch = ''
263 cd ./changehat/pam_apparmor
264 '';
265 makeFlags = [ "USE_SYSTEM=1" ];
266 installFlags = [ "DESTDIR=$(out)" ];
267
268 inherit doCheck;
269
270 meta = apparmor-meta "PAM service";
271 };
272
273 apparmor-profiles = stdenv.mkDerivation {
274 pname = "apparmor-profiles";
275 version = apparmor-version;
276
277 src = apparmor-sources;
278
279 nativeBuildInputs = [ which ];
280
281 postPatch = ''
282 cd ./profiles
283 '';
284
285 installFlags = [ "DESTDIR=$(out)" "EXTRAS_DEST=$(out)/share/apparmor/extra-profiles" ];
286
287 inherit doCheck;
288
289 meta = apparmor-meta "profiles";
290 };
291
292 apparmor-kernel-patches = stdenv.mkDerivation {
293 pname = "apparmor-kernel-patches";
294 version = apparmor-version;
295
296 src = apparmor-sources;
297
298 dontBuild = true;
299
300 installPhase = ''
301 mkdir "$out"
302 cp -R ./kernel-patches/* "$out"
303 '';
304
305 inherit doCheck;
306
307 meta = apparmor-meta "kernel patches";
308 };
309
310 # Generate generic AppArmor rules in a file, from the closure of given
311 # rootPaths. To be included in an AppArmor profile like so:
312 #
313 # include "${apparmorRulesFromClosure { } [ pkgs.hello ]}"
314 apparmorRulesFromClosure =
315 { # The store path of the derivation is given in $path
316 additionalRules ? []
317 # TODO: factorize here some other common paths
318 # that may emerge from use cases.
319 , baseRules ? [
320 "r $path"
321 "r $path/etc/**"
322 "r $path/share/**"
323 # Note that not all libraries are prefixed with "lib",
324 # eg. glibc-2.30/lib/ld-2.30.so
325 "mr $path/lib/**.so*"
326 # eg. glibc-2.30/lib/gconv/gconv-modules
327 "r $path/lib/**"
328 ]
329 , name ? ""
330 }: rootPaths: runCommand
331 ( "apparmor-closure-rules"
332 + lib.optionalString (name != "") "-${name}" ) {} ''
333 touch $out
334 while read -r path
335 do printf >>$out "%s,\n" ${lib.concatMapStringsSep " " (x: "\"${x}\"") (baseRules ++ additionalRules)}
336 done <${closureInfo { inherit rootPaths; }}/store-paths
337 '';
338in
339{
340 inherit
341 libapparmor
342 apparmor-utils
343 apparmor-bin-utils
344 apparmor-parser
345 apparmor-pam
346 apparmor-profiles
347 apparmor-kernel-patches
348 apparmorRulesFromClosure;
349}