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.6";
26
27 apparmor-meta = component: with lib; {
28 homepage = "https://apparmor.net/";
29 description = "A mandatory access control system - ${component}";
30 license = with licenses; [ gpl2Only lgpl21Only ];
31 maintainers = with maintainers; [ julm thoughtpolice ajs124 ];
32 platforms = platforms.linux;
33 };
34
35 apparmor-sources = fetchFromGitLab {
36 owner = "apparmor";
37 repo = "apparmor";
38 rev = "v${apparmor-version}";
39 hash = "sha256-VPgRmmQv+kgLduc6RTu9gotyjT6OImUXsPeatgG7m9E=";
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 = python.pkgs.buildPythonApplication {
132 pname = "apparmor-utils";
133 version = apparmor-version;
134 format = "other";
135
136 src = apparmor-sources;
137
138 strictDeps = true;
139
140 nativeBuildInputs = [ makeWrapper which python ];
141
142 buildInputs = [
143 bash
144 perl
145 python
146 libapparmor
147 (libapparmor.python or null)
148 ];
149
150 propagatedBuildInputs = [
151 libapparmor.python
152
153 # Used by aa-notify
154 python.pkgs.notify2
155 python.pkgs.psutil
156 ];
157
158 prePatch = prePatchCommon +
159 # Do not build vim file
160 lib.optionalString stdenv.hostPlatform.isMusl ''
161 sed -i ./utils/Makefile -e "/\<vim\>/d"
162 '' + ''
163 sed -i -E 's/^(DESTDIR|BINDIR|PYPREFIX)=.*//g' ./utils/Makefile
164
165 sed -i utils/aa-unconfined -e "/my_env\['PATH'\]/d"
166
167 substituteInPlace utils/aa-remove-unknown \
168 --replace "/lib/apparmor/rc.apparmor.functions" "${apparmor-parser}/lib/apparmor/rc.apparmor.functions"
169 '';
170 inherit patches;
171 postPatch = "cd ./utils";
172 makeFlags = [ "LANGS=" ];
173 installFlags = [ "DESTDIR=$(out)" "BINDIR=$(out)/bin" "VIM_INSTALL_PATH=$(out)/share" "PYPREFIX=" ];
174
175 postInstall = ''
176 wrapProgram $out/bin/aa-remove-unknown \
177 --prefix PATH : ${lib.makeBinPath [ gawk ]}
178
179 ln -s ${aa-teardown} $out/bin/aa-teardown
180 '';
181
182 inherit doCheck;
183
184 meta = apparmor-meta "user-land utilities" // {
185 broken = !(withPython && withPerl);
186 };
187 };
188
189 apparmor-bin-utils = stdenv.mkDerivation {
190 pname = "apparmor-bin-utils";
191 version = apparmor-version;
192
193 src = apparmor-sources;
194
195 nativeBuildInputs = [
196 pkg-config
197 libapparmor
198 which
199 ];
200
201 buildInputs = [
202 libapparmor
203 ];
204
205 prePatch = prePatchCommon;
206 postPatch = ''
207 cd ./binutils
208 '';
209 makeFlags = [ "LANGS=" "USE_SYSTEM=1" ];
210 installFlags = [ "DESTDIR=$(out)" "BINDIR=$(out)/bin" "SBINDIR=$(out)/bin" ];
211
212 inherit doCheck;
213
214 meta = apparmor-meta "binary user-land utilities";
215 };
216
217 apparmor-parser = stdenv.mkDerivation {
218 pname = "apparmor-parser";
219 version = apparmor-version;
220
221 src = apparmor-sources;
222
223 nativeBuildInputs = [ bison flex which ];
224
225 buildInputs = [ libapparmor ];
226
227 prePatch = prePatchCommon + ''
228 ## techdoc.pdf still doesn't build ...
229 substituteInPlace ./parser/Makefile \
230 --replace "/usr/bin/bison" "${bison}/bin/bison" \
231 --replace "/usr/bin/flex" "${flex}/bin/flex" \
232 --replace "/usr/include/linux/capability.h" "${linuxHeaders}/include/linux/capability.h" \
233 --replace "manpages htmlmanpages pdf" "manpages htmlmanpages"
234 substituteInPlace parser/rc.apparmor.functions \
235 --replace "/sbin/apparmor_parser" "$out/bin/apparmor_parser"
236 sed -i parser/rc.apparmor.functions -e '2i . ${./fix-rc.apparmor.functions.sh}'
237 '';
238 inherit patches;
239 postPatch = ''
240 cd ./parser
241 '';
242 makeFlags = [
243 "LANGS=" "USE_SYSTEM=1" "INCLUDEDIR=${libapparmor}/include"
244 "AR=${stdenv.cc.bintools.targetPrefix}ar"
245 ];
246 installFlags = [ "DESTDIR=$(out)" "DISTRO=unknown" ];
247
248 inherit doCheck;
249
250 meta = apparmor-meta "rule parser";
251 };
252
253 apparmor-pam = stdenv.mkDerivation {
254 pname = "apparmor-pam";
255 version = apparmor-version;
256
257 src = apparmor-sources;
258
259 nativeBuildInputs = [ pkg-config which ];
260
261 buildInputs = [ libapparmor pam ];
262
263 postPatch = ''
264 cd ./changehat/pam_apparmor
265 '';
266 makeFlags = [ "USE_SYSTEM=1" ];
267 installFlags = [ "DESTDIR=$(out)" ];
268
269 inherit doCheck;
270
271 meta = apparmor-meta "PAM service";
272 };
273
274 apparmor-profiles = stdenv.mkDerivation {
275 pname = "apparmor-profiles";
276 version = apparmor-version;
277
278 src = apparmor-sources;
279
280 nativeBuildInputs = [ which ];
281
282 postPatch = ''
283 cd ./profiles
284 '';
285
286 installFlags = [ "DESTDIR=$(out)" "EXTRAS_DEST=$(out)/share/apparmor/extra-profiles" ];
287
288 inherit doCheck;
289
290 meta = apparmor-meta "profiles";
291 };
292
293 apparmor-kernel-patches = stdenv.mkDerivation {
294 pname = "apparmor-kernel-patches";
295 version = apparmor-version;
296
297 src = apparmor-sources;
298
299 dontBuild = true;
300
301 installPhase = ''
302 mkdir "$out"
303 cp -R ./kernel-patches/* "$out"
304 '';
305
306 inherit doCheck;
307
308 meta = apparmor-meta "kernel patches";
309 };
310
311 # Generate generic AppArmor rules in a file, from the closure of given
312 # rootPaths. To be included in an AppArmor profile like so:
313 #
314 # include "${apparmorRulesFromClosure { } [ pkgs.hello ]}"
315 apparmorRulesFromClosure =
316 { # The store path of the derivation is given in $path
317 additionalRules ? []
318 # TODO: factorize here some other common paths
319 # that may emerge from use cases.
320 , baseRules ? [
321 "r $path"
322 "r $path/etc/**"
323 "r $path/share/**"
324 # Note that not all libraries are prefixed with "lib",
325 # eg. glibc-2.30/lib/ld-2.30.so
326 "mr $path/lib/**.so*"
327 # eg. glibc-2.30/lib/gconv/gconv-modules
328 "r $path/lib/**"
329 ]
330 , name ? ""
331 }: rootPaths: runCommand
332 ( "apparmor-closure-rules"
333 + lib.optionalString (name != "") "-${name}" ) {} ''
334 touch $out
335 while read -r path
336 do printf >>$out "%s,\n" ${lib.concatMapStringsSep " " (x: "\"${x}\"") (baseRules ++ additionalRules)}
337 done <${closureInfo { inherit rootPaths; }}/store-paths
338 '';
339in
340{
341 inherit
342 libapparmor
343 apparmor-utils
344 apparmor-bin-utils
345 apparmor-parser
346 apparmor-pam
347 apparmor-profiles
348 apparmor-kernel-patches
349 apparmorRulesFromClosure;
350}