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