Merge lp:~mdeslaur/upstart/apparmor-support into lp:upstart

Proposed by Marc Deslauriers
Status: Merged
Merged at revision: 1476
Proposed branch: lp:~mdeslaur/upstart/apparmor-support
Merge into: lp:upstart
Diff against target: 1899 lines (+1133/-199)
21 files modified
ChangeLog (+52/-0)
init/Makefile.am (+16/-15)
init/apparmor.c (+116/-0)
init/apparmor.h (+52/-0)
init/errors.h (+3/-0)
init/job.c (+64/-8)
init/job.h (+1/-0)
init/job_class.c (+13/-0)
init/job_class.h (+3/-0)
init/job_process.c (+212/-170)
init/job_process.h (+2/-1)
init/man/init.5 (+32/-0)
init/parse_job.c (+109/-0)
init/process.c (+6/-0)
init/process.h (+1/-0)
init/tests/data/upstart-pre-security.json (+1/-0)
init/tests/test_job.c (+36/-5)
init/tests/test_job_class.c (+2/-0)
init/tests/test_parse_job.c (+263/-0)
init/tests/test_process.c (+14/-0)
init/tests/test_state.c (+135/-0)
To merge this branch: bzr merge lp:~mdeslaur/upstart/apparmor-support
Reviewer Review Type Date Requested Status
James Hunt Approve
Stéphane Graber (community) Approve
Marc Deslauriers (community) Needs Resubmitting
Review via email: mp+164169@code.launchpad.net

Description of the change

This merge request adds native AppArmor support in Upstart by introducing a new process type that can be shared with other Mandatory Access Control frameworks.

The new AppArmor stanzas allow specifying an AppArmor profile to load at job start, and a profile to switch to before running the main process. One interesting use case for switching AppArmor profiles is to confine applications started with jobs in User Session mode without relying on automatic path attachment.

If the running kernel doesn't have AppArmor enabled, or if the AppArmor tools aren't installed, the stanzas are simply ignored, allowing package maintainers to include AppArmor profiles and stanzas in their packages that will work on both distros that enable AppArmor by default, and distros that don't.

To post a comment you must log in.
Revision history for this message
James Hunt (jamesodhunt) wrote :

This is excellent. I really like the way you've introduced the new security process type as it is extensible and allows any apparmor_parser failure output to be logged automatically ;-)

My only comment is that we could do with on more test to assert that we can handle reading an "entire" state file that does _not_ contain the new AppArmor-related serialisation data. Specifically, can you add an explicit test to init/tests/test_state.c:test_data_files that:

1) Reads in a prepared JSON file without the AppArmor content.
2) Asserts 'assert0 (state_from_string (json_string));'
3) Checks that JobClass->apparmor_switch has a reasonable value.
4) Checks that Job->pid[PROCESS_SECURITY] has a reasonable value.

Also, a reminder-to-self:

Once lp:~jamesodhunt/upstart/serialise-remaining-objects lands, we'll need to bump STATE_VERSION to take account of the new serialisation format (PROCESS_SECURITY and JobClass->apparmor_switch).

1478. By Marc Deslauriers

init/process.c: Fix deserialising with PROCESS_SECURITY.

1479. By Marc Deslauriers

Added test for serialisation data with new security elements.

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

Excellent comment. I've added the requested test, and it uncovered a bug with deserialisation, which I've fixed also. Thanks!

review: Needs Resubmitting
1480. By Marc Deslauriers

An unused process is supposed to be NULL. Fix deserialiser and test.

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

Testing uncovered an issue when deserialising state from an older version. Fixed.

review: Needs Resubmitting
1481. By Marc Deslauriers

Allow environment variables in apparmor switch stanzas.

Revision history for this message
Stéphane Graber (stgraber) wrote :

Hi,

Thanks for this work. I had a quick look through the merge proposal and it looks pretty good.

I just have a question regarding "apparmor load". It's currently restricted to non-usermode presumably because you want to ensure that the user is indeed allowed to load a new profile into apparmor.

So what happens when you run upstart without the usermode but as a user (init --session)?

Also, is there any reason not to allow loading extra apparmor profiles when running in user mode with the user being root?

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

> So what happens when you run upstart without the usermode but as a user (init --session)?

> Also, is there any reason not to allow loading extra apparmor profiles when running in user mode with the user being root?

I didn't realize these were common scenarios.

Perhaps it should simply fail a job if it contains an "apparmor load" stanza and the user doesn't have appropriate permissions?

Revision history for this message
Stéphane Graber (stgraber) wrote :

I think that'd make sense, that's how setuid/setgid and other privileged stanzas behave currently so it'd be consistent.

Running init against the session bus and without user mode isn't very common, the biggest use case for this is development.

The use case for running a session init as root isn't yet present in Ubuntu but may be in the relatively near future where we want to have upstart spawned by a PAM plugin even for text consoles (exact details are yet to be specced) so in that case you'd get a Session Init for the root user.

1482. By Marc Deslauriers

Don't check for user mode when trying to load an AppArmor profile.
User mode is supported as root, and upstart can be run as a user
without being in user mode.

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

OK, that makes sense. I've removed the check for user mode. Thanks.

review: Needs Resubmitting
Revision history for this message
Stéphane Graber (stgraber) wrote :

Shouldn't the manpage explicitly say that "apparmor load" requires root privileges (or at least cap_mac_admin?) while "apparmor switch" will work even when upstart runs as an unprivileged user as long as the profile is already loaded?

Revision history for this message
Marc Deslauriers (mdeslaur) wrote :

I'm not sure. Future versions of AppArmor may allow unprivileged users to load a new profile, and switching to a different profile may fail if the user is already confined by a restrictive AppArmor profile (with pam_apparmor for example). Also, I don't see any other mention of privileges in the man page, such as the setuid/setgid stanzas. While I could potentially add a "requires appropriate privileges" blurb, I believe it's assumed everywhere else.

Revision history for this message
Stéphane Graber (stgraber) wrote :

Fair enough.

review: Approve
Revision history for this message
James Hunt (jamesodhunt) wrote :

Thanks Marc. Merged.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'ChangeLog'
2--- ChangeLog 2013-05-07 08:59:05 +0000
3+++ ChangeLog 2013-05-27 14:31:27 +0000
4@@ -1,3 +1,55 @@
5+2013-05-27 Marc Deslauriers <marc.deslauriers@ubuntu.com>
6+
7+ * init/job.c: Don't check for user mode when trying to load an
8+ AppArmor profile. User mode is supported as root, and upstart
9+ can be run as a user without being in user mode.
10+ * init/man/init.5: Adjust man page.
11+
12+2013-05-24 Marc Deslauriers <marc.deslauriers@ubuntu.com>
13+
14+ * init/job_process.c: Allow environment variables in apparmor
15+ switch stanzas.
16+ * init/apparmor.[ch]: Use a profile name instead of a Job so we
17+ can use environment variables.
18+
19+2013-05-23 Marc Deslauriers <marc.deslauriers@ubuntu.com>
20+
21+ * init/tests/test_state.c: An unused process is actually supposed
22+ to be NULL. Fix test.
23+ * init/process.c: Adjust to leave unused process as NULL.
24+
25+2013-05-17 Marc Deslauriers <marc.deslauriers@ubuntu.com>
26+
27+ * init/process.c: Fix deserialising with PROCESS_SECURITY.
28+ * init/tests/data/upstart-pre-security.json: Added new file to
29+ test importing serialisation data without security elements.
30+ * init/tests/test_state.c: Added new data format test.
31+
32+2013-05-15 Marc Deslauriers <marc.deslauriers@ubuntu.com>
33+
34+ * init/apparmor.[ch]: AppArmor profile helper.
35+ * init/Makefile.am: Added AppArmor profile helper.
36+ * init/errors.h: Added SECURITY_ERROR.
37+ * init/job.c:
38+ - Added new JOB_SECURITY state and PROCESS_SECURITY process.
39+ - Fix job_deserialise() for new PROCESS_SECURITY process.
40+ * init/job.h: Added new JOB_SECURITY state.
41+ * init/job_class.[ch]: Added apparmor_switch to hold the new
42+ "apparmor switch" stanza.
43+ * init/job_process.c:
44+ - Switch to new AppArmor profile.
45+ - Handle PROCESS_SECURITY process.
46+ * init/job_process.h: Added JOB_PROCESS_ERROR_SECURITY.
47+ * init/man/init.5: Document new AppArmor stanzas.
48+ * init/parse_job.c: Parse new "apparmor" stanzas.
49+ * init/process.[ch]: Add PROCESS_SECURITY.
50+ * init/tests/test_job.c: Add new tests, and adjust existing ones.
51+ * init/tests/test_job_class.c: Added apparmor_switch.
52+ * init/tests/test_parse_job.c: Test new AppArmor stanza parsing.
53+ * init/tests/test_process.c: Added PROCESS_SECURITY tests.
54+ * init/tests/test_state.c: Test apparmor_switch and
55+ PROCESS_SECURITY.
56+
57 2013-05-07 James Hunt <james.hunt@ubuntu.com>
58
59 * util/man/shutdown.8: Specify default action is to bring system
60
61=== modified file 'init/Makefile.am'
62--- init/Makefile.am 2013-04-17 09:41:42 +0000
63+++ init/Makefile.am 2013-05-27 14:31:27 +0000
64@@ -59,7 +59,8 @@
65 control.c control.h \
66 xdg.c xdg.h \
67 quiesce.c quiesce.h \
68- errors.h
69+ errors.h \
70+ apparmor.c apparmor.h
71 nodist_init_SOURCES = \
72 $(com_ubuntu_Upstart_OUTPUTS) \
73 $(com_ubuntu_Upstart_Job_OUTPUTS) \
74@@ -186,7 +187,7 @@
75 system.o environ.o process.o \
76 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
77 parse_job.o parse_conf.o conf.o control.o quiesce.o \
78- session.o log.o state.o xdg.o \
79+ session.o log.o state.o xdg.o apparmor.o \
80 com.ubuntu.Upstart.o \
81 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
82 $(NIH_LIBS) \
83@@ -200,7 +201,7 @@
84 system.o environ.o process.o \
85 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
86 parse_job.o parse_conf.o conf.o control.o quiesce.o \
87- session.o log.o state.o xdg.o \
88+ session.o log.o state.o xdg.o apparmor.o \
89 com.ubuntu.Upstart.o \
90 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
91 $(NIH_LIBS) \
92@@ -214,7 +215,7 @@
93 system.o environ.o process.o \
94 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
95 parse_job.o parse_conf.o conf.o control.o quiesce.o \
96- session.o log.o state.o xdg.o \
97+ session.o log.o state.o xdg.o apparmor.o \
98 com.ubuntu.Upstart.o \
99 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
100 $(NIH_LIBS) \
101@@ -228,7 +229,7 @@
102 system.o environ.o process.o \
103 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
104 parse_job.o parse_conf.o conf.o control.o quiesce.o \
105- session.o log.o state.o xdg.o \
106+ session.o log.o state.o xdg.o apparmor.o \
107 com.ubuntu.Upstart.o \
108 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
109 $(NIH_LIBS) \
110@@ -242,7 +243,7 @@
111 system.o environ.o process.o \
112 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
113 parse_job.o parse_conf.o conf.o control.o quiesce.o \
114- session.o log.o state.o xdg.o \
115+ session.o log.o state.o xdg.o apparmor.o \
116 com.ubuntu.Upstart.o \
117 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
118 $(NIH_LIBS) \
119@@ -256,7 +257,7 @@
120 system.o environ.o process.o \
121 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
122 parse_job.o parse_conf.o conf.o control.o quiesce.o \
123- session.o log.o state.o xdg.o \
124+ session.o log.o state.o xdg.o apparmor.o \
125 com.ubuntu.Upstart.o \
126 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
127 $(NIH_LIBS) \
128@@ -270,7 +271,7 @@
129 system.o environ.o process.o \
130 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
131 parse_job.o parse_conf.o conf.o control.o quiesce.o \
132- session.o log.o state.o xdg.o \
133+ session.o log.o state.o xdg.o apparmor.o \
134 com.ubuntu.Upstart.o \
135 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
136 $(NIH_LIBS) \
137@@ -284,7 +285,7 @@
138 system.o environ.o process.o \
139 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
140 parse_job.o parse_conf.o conf.o control.o quiesce.o \
141- session.o log.o state.o xdg.o \
142+ session.o log.o state.o xdg.o apparmor.o \
143 com.ubuntu.Upstart.o \
144 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
145 $(NIH_LIBS) \
146@@ -298,7 +299,7 @@
147 system.o environ.o process.o \
148 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
149 parse_job.o parse_conf.o conf.o control.o quiesce.o \
150- session.o log.o state.o xdg.o \
151+ session.o log.o state.o xdg.o apparmor.o \
152 com.ubuntu.Upstart.o \
153 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
154 $(NIH_LIBS) \
155@@ -312,7 +313,7 @@
156 system.o environ.o process.o \
157 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
158 parse_job.o parse_conf.o conf.o control.o quiesce.o \
159- session.o log.o state.o xdg.o \
160+ session.o log.o state.o xdg.o apparmor.o \
161 com.ubuntu.Upstart.o \
162 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
163 $(NIH_LIBS) \
164@@ -326,7 +327,7 @@
165 system.o environ.o process.o \
166 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
167 parse_job.o parse_conf.o conf.o control.o quiesce.o \
168- session.o log.o state.o xdg.o \
169+ session.o log.o state.o xdg.o apparmor.o \
170 com.ubuntu.Upstart.o \
171 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
172 $(NIH_LIBS) \
173@@ -345,7 +346,7 @@
174 system.o environ.o process.o \
175 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
176 parse_job.o parse_conf.o conf.o control.o quiesce.o \
177- session.o log.o state.o xdg.o \
178+ session.o log.o state.o xdg.o apparmor.o \
179 com.ubuntu.Upstart.o \
180 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
181 $(NIH_LIBS) \
182@@ -359,7 +360,7 @@
183 system.o environ.o process.o \
184 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
185 parse_job.o parse_conf.o control.o quiesce.o \
186- session.o log.o state.o xdg.o \
187+ session.o log.o state.o xdg.o apparmor.o \
188 com.ubuntu.Upstart.o \
189 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
190 $(NIH_LIBS) \
191@@ -380,7 +381,7 @@
192 system.o environ.o process.o \
193 job_class.o job_process.o job.o event.o event_operator.o blocked.o \
194 parse_job.o parse_conf.o conf.o control.o quiesce.o \
195- session.o log.o state.o xdg.o \
196+ session.o log.o state.o xdg.o apparmor.o \
197 com.ubuntu.Upstart.o \
198 com.ubuntu.Upstart.Job.o com.ubuntu.Upstart.Instance.o \
199 $(NIH_LIBS) \
200
201=== added file 'init/apparmor.c'
202--- init/apparmor.c 1970-01-01 00:00:00 +0000
203+++ init/apparmor.c 2013-05-27 14:31:27 +0000
204@@ -0,0 +1,116 @@
205+/* upstart
206+ *
207+ * apparmor.c - handle AppArmor profiles
208+ *
209+ * Copyright © 2013 Canonical Ltd.
210+ * Author: Marc Deslauriers <marc.deslauriers@canonical.com>.
211+ *
212+ * This program is free software; you can redistribute it and/or modify
213+ * it under the terms of the GNU General Public License version 2, as
214+ * published by the Free Software Foundation.
215+ *
216+ * This program is distributed in the hope that it will be useful,
217+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
218+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
219+ * GNU General Public License for more details.
220+ *
221+ * You should have received a copy of the GNU General Public License along
222+ * with this program; if not, write to the Free Software Foundation, Inc.,
223+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
224+ */
225+
226+#ifdef HAVE_CONFIG_H
227+# include <config.h>
228+#endif /* HAVE_CONFIG_H */
229+
230+#include <sys/types.h>
231+#include <sys/stat.h>
232+
233+#include <stdio.h>
234+#include <unistd.h>
235+#include <limits.h>
236+
237+#include <nih/signal.h>
238+#include <nih/string.h>
239+
240+#include "apparmor.h"
241+
242+/**
243+ * apparmor_switch:
244+ * @profile: AppArmor profile to switch to
245+ *
246+ * This function switches to a new AppArmor profile on exec
247+ *
248+ * Returns: zero on success, -1 on error
249+ **/
250+int
251+apparmor_switch (char *profile)
252+{
253+ nih_local char *filename = NULL;
254+ FILE *f;
255+
256+ nih_assert (profile != NULL);
257+
258+ /* Silently fail if AppArmor isn't enabled. */
259+ if (! apparmor_available())
260+ return 0;
261+
262+ filename = nih_sprintf (NULL, "/proc/%d/attr/exec", getpid());
263+
264+ if (! filename)
265+ return -1;
266+
267+ f = fopen (filename, "w");
268+
269+ if (! f)
270+ return -1;
271+
272+ fprintf (f, "exec %s\n", profile);
273+
274+ if (fclose (f))
275+ return -1;
276+
277+ return 0;
278+}
279+
280+/**
281+ * apparmor_available:
282+ *
283+ * This function checks to see if AppArmor is available and enabled
284+ *
285+ * Returns: TRUE if AppArmor is available, FALSE if it isn't
286+ **/
287+int
288+apparmor_available (void)
289+{
290+ struct stat statbuf;
291+ FILE *f;
292+ int value = 0;
293+
294+ /* Do not load if AppArmor is disabled.
295+ */
296+ f = fopen ("/sys/module/apparmor/parameters/enabled", "r");
297+
298+ if (! f)
299+ return FALSE;
300+
301+ value = fgetc (f);
302+
303+ if (fclose (f))
304+ return FALSE;
305+
306+ if (value != 'Y')
307+ return FALSE;
308+
309+ /* Do not load if AppArmor parser isn't available.
310+ */
311+ if (stat (APPARMOR_PARSER, &statbuf) == 0) {
312+ if(! (S_ISREG(statbuf.st_mode) && statbuf.st_mode & S_IXUSR))
313+ return FALSE;
314+ } else {
315+ return FALSE;
316+ }
317+
318+ return TRUE;
319+}
320+
321
322=== added file 'init/apparmor.h'
323--- init/apparmor.h 1970-01-01 00:00:00 +0000
324+++ init/apparmor.h 2013-05-27 14:31:27 +0000
325@@ -0,0 +1,52 @@
326+/* upstart
327+ *
328+ * Copyright © 2013 Canonical Ltd.
329+ * Author: Marc Deslauriers <marc.deslauriers@canonical.com>.
330+ *
331+ * This program is free software; you can redistribute it and/or modify
332+ * it under the terms of the GNU General Public License version 2, as
333+ * published by the Free Software Foundation.
334+ *
335+ * This program is distributed in the hope that it will be useful,
336+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
337+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
338+ * GNU General Public License for more details.
339+ *
340+ * You should have received a copy of the GNU General Public License along
341+ * with this program; if not, write to the Free Software Foundation, Inc.,
342+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
343+ */
344+
345+#ifndef INIT_APPARMOR_H
346+#define INIT_APPARMOR_H
347+
348+#include "job.h"
349+
350+/**
351+ * APPARMOR_PARSER:
352+ *
353+ * Location of apparmor_parser binary
354+ *
355+ **/
356+#define APPARMOR_PARSER "/sbin/apparmor_parser"
357+
358+/**
359+ * APPARMOR_PARSER_OPTS:
360+ *
361+ * apparmor_parser options
362+ *
363+ **/
364+#define APPARMOR_PARSER_OPTS "-r -W"
365+
366+
367+NIH_BEGIN_EXTERN
368+
369+int apparmor_switch (char *profile)
370+ __attribute__ ((warn_unused_result));
371+
372+int apparmor_available (void)
373+ __attribute__ ((warn_unused_result));
374+
375+NIH_END_EXTERN
376+
377+#endif /* INIT_APPARMOR_H */
378
379=== modified file 'init/errors.h'
380--- init/errors.h 2012-09-09 21:38:59 +0000
381+++ init/errors.h 2013-05-27 14:31:27 +0000
382@@ -54,6 +54,9 @@
383
384 /* Errors while handling control requests */
385 CONTROL_NAME_TAKEN,
386+
387+ /* Errors while handling security profiles */
388+ SECURITY_ERROR,
389 };
390
391 /* Error strings for defined messages */
392
393=== modified file 'init/job.c'
394--- init/job.c 2013-02-27 11:46:04 +0000
395+++ init/job.c 2013-05-27 14:31:27 +0000
396@@ -56,6 +56,7 @@
397 #include "control.h"
398 #include "parse_job.h"
399 #include "state.h"
400+#include "apparmor.h"
401
402 #include "com.ubuntu.Upstart.Job.h"
403 #include "com.ubuntu.Upstart.Instance.h"
404@@ -397,9 +398,25 @@
405 job->blocker = job_emit_event (job);
406
407 break;
408+ case JOB_SECURITY:
409+ nih_assert (job->goal == JOB_START);
410+ nih_assert (old_state == JOB_STARTING);
411+
412+ if (job->class->process[PROCESS_SECURITY]
413+ && apparmor_available()) {
414+ if (job_process_run (job, PROCESS_SECURITY) < 0) {
415+ job_failed (job, PROCESS_SECURITY, -1);
416+ job_change_goal (job, JOB_STOP);
417+ state = job_next_state (job);
418+ }
419+ } else {
420+ state = job_next_state (job);
421+ }
422+
423+ break;
424 case JOB_PRE_START:
425 nih_assert (job->goal == JOB_START);
426- nih_assert (old_state == JOB_STARTING);
427+ nih_assert (old_state == JOB_SECURITY);
428
429 if (job->class->process[PROCESS_PRE_START]) {
430 if (job_process_run (job, PROCESS_PRE_START) < 0) {
431@@ -480,6 +497,7 @@
432 case JOB_STOPPING:
433 nih_assert ((old_state == JOB_STARTING)
434 || (old_state == JOB_PRE_START)
435+ || (old_state == JOB_SECURITY)
436 || (old_state == JOB_SPAWNED)
437 || (old_state == JOB_POST_START)
438 || (old_state == JOB_RUNNING)
439@@ -598,6 +616,15 @@
440 case JOB_STOP:
441 return JOB_STOPPING;
442 case JOB_START:
443+ return JOB_SECURITY;
444+ default:
445+ nih_assert_not_reached ();
446+ }
447+ case JOB_SECURITY:
448+ switch (job->goal) {
449+ case JOB_STOP:
450+ return JOB_STOPPING;
451+ case JOB_START:
452 return JOB_PRE_START;
453 default:
454 nih_assert_not_reached ();
455@@ -1072,6 +1099,8 @@
456 return N_("waiting");
457 case JOB_STARTING:
458 return N_("starting");
459+ case JOB_SECURITY:
460+ return N_("security");
461 case JOB_PRE_START:
462 return N_("pre-start");
463 case JOB_SPAWNED:
464@@ -1110,6 +1139,8 @@
465 return JOB_WAITING;
466 } else if (! strcmp (state, "starting")) {
467 return JOB_STARTING;
468+ } else if (! strcmp (state, "security")) {
469+ return JOB_SECURITY;
470 } else if (! strcmp (state, "pre-start")) {
471 return JOB_PRE_START;
472 } else if (! strcmp (state, "spawned")) {
473@@ -1926,8 +1957,21 @@
474 if (ret < 0)
475 goto error;
476
477- if (len != PROCESS_LAST)
478+ /* If we are missing one, we're probably importing from a
479+ * previous version that didn't include PROCESS_SECURITY.
480+ * Simply add the missing one.
481+ */
482+ if (len == PROCESS_LAST - 1) {
483+ job->pid = nih_realloc (job->pid, job, sizeof (pid_t) * PROCESS_LAST);
484+
485+ if (! job->pid)
486+ goto error;
487+
488+ job->pid[PROCESS_LAST - 1] = 0;
489+
490+ } else if (len != PROCESS_LAST) {
491 goto error;
492+ }
493
494 if (! state_get_json_int_var_to_obj (json, job, trace_forks))
495 goto error;
496@@ -1949,13 +1993,23 @@
497 json_object *json_log;
498
499 json_log = json_object_array_get_idx (json_logs, process);
500- if (! json_log)
501- goto error;
502
503- /* NULL if there was no log configured, or we failed to
504- * deserialise it; either way, this should be non-fatal.
505- */
506- job->log[process] = log_deserialise (job->log, json_log);
507+ if (json_log) {
508+ /* NULL if there was no log configured, or we failed to
509+ * deserialise it; either way, this should be non-fatal.
510+ */
511+ job->log[process] = log_deserialise (job->log, json_log);
512+ } else {
513+ /* If we are missing one, we're probably importing from a
514+ * previous version that didn't include PROCESS_SECURITY.
515+ * Simply ignore the missing one.
516+ */
517+ if (process == PROCESS_LAST - 1) {
518+ job->log[process] = NULL;
519+ } else {
520+ goto error;
521+ }
522+ }
523 }
524
525 return job;
526@@ -2066,6 +2120,7 @@
527 {
528 state_enum_to_str (JOB_WAITING, state);
529 state_enum_to_str (JOB_STARTING, state);
530+ state_enum_to_str (JOB_SECURITY, state);
531 state_enum_to_str (JOB_PRE_START, state);
532 state_enum_to_str (JOB_SPAWNED, state);
533 state_enum_to_str (JOB_POST_START, state);
534@@ -2092,6 +2147,7 @@
535 {
536 state_str_to_enum (JOB_WAITING, state);
537 state_str_to_enum (JOB_STARTING, state);
538+ state_str_to_enum (JOB_SECURITY, state);
539 state_str_to_enum (JOB_PRE_START, state);
540 state_str_to_enum (JOB_SPAWNED, state);
541 state_str_to_enum (JOB_POST_START, state);
542
543=== modified file 'init/job.h'
544--- init/job.h 2013-02-27 11:46:04 +0000
545+++ init/job.h 2013-05-27 14:31:27 +0000
546@@ -66,6 +66,7 @@
547 typedef enum job_state {
548 JOB_WAITING,
549 JOB_STARTING,
550+ JOB_SECURITY,
551 JOB_PRE_START,
552 JOB_SPAWNED,
553 JOB_POST_START,
554
555=== modified file 'init/job_class.c'
556--- init/job_class.c 2013-02-25 09:38:55 +0000
557+++ init/job_class.c 2013-05-27 14:31:27 +0000
558@@ -362,6 +362,8 @@
559
560 class->usage = NULL;
561
562+ class->apparmor_switch = NULL;
563+
564 return class;
565
566 error:
567@@ -1873,6 +1875,9 @@
568 if (! state_set_json_string_var_from_obj (json, class, usage))
569 goto error;
570
571+ if (! state_set_json_string_var_from_obj (json, class, apparmor_switch))
572+ goto error;
573+
574 return json;
575
576 error:
577@@ -2109,6 +2114,14 @@
578 if (! state_get_json_string_var_to_obj (json, class, usage))
579 goto error;
580
581+ /* If we are missing this, we're probably importing from a
582+ * previous version that didn't include PROCESS_SECURITY.
583+ */
584+ if (json_object_object_get (json, "apparmor_switch")) {
585+ if (! state_get_json_string_var_to_obj (json, class, apparmor_switch))
586+ goto error;
587+ }
588+
589 json_normalexit = json_object_object_get (json, "normalexit");
590 if (! json_normalexit)
591 goto error;
592
593=== modified file 'init/job_class.h'
594--- init/job_class.h 2013-02-27 11:46:04 +0000
595+++ init/job_class.h 2013-05-27 14:31:27 +0000
596@@ -164,6 +164,7 @@
597 * @setgid: group name to drop to before starting process,
598 * @deleted: whether job should be deleted when finished.
599 * @usage: usage text - how to control job
600+ * @apparmor_switch: AppArmor profile to switch to before starting job
601 *
602 * This structure holds the configuration of a known task or service that
603 * should be tracked by the init daemon; as tasks and services are
604@@ -220,6 +221,8 @@
605 int debug;
606
607 char *usage;
608+
609+ char *apparmor_switch;
610 } JobClass;
611
612
613
614=== modified file 'init/job_process.c'
615--- init/job_process.c 2013-03-28 17:07:54 +0000
616+++ init/job_process.c 2013-05-27 14:31:27 +0000
617@@ -67,6 +67,7 @@
618 #include "errors.h"
619 #include "control.h"
620 #include "xdg.h"
621+#include "apparmor.h"
622
623
624 /**
625@@ -701,184 +702,208 @@
626 close (pty_slave);
627 }
628
629- /* Set resource limits for the process, skipping over any that
630- * aren't set in the job class such that they inherit from
631- * ourselves (and we inherit from kernel defaults).
632+ /* Switch to the specified AppArmor profile, but only for the main
633+ process, so we don't confine the pre- and post- processes.
634 */
635- for (i = 0; i < RLIMIT_NLIMITS; i++) {
636- if (! class->limits[i])
637- continue;
638-
639- if (setrlimit (i, class->limits[i]) < 0) {
640+ if ((class->apparmor_switch) && (process == PROCESS_MAIN)) {
641+ nih_local char *profile = NULL;
642+
643+ /* Use the environment to expand the AppArmor profile name
644+ */
645+ profile = NIH_SHOULD (environ_expand (NULL,
646+ class->apparmor_switch,
647+ environ));
648+
649+ if (! profile) {
650+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SECURITY, 0);
651+ }
652+
653+ if (apparmor_switch (profile) < 0) {
654+ nih_error_raise_system ();
655+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SECURITY, 0);
656+ }
657+ }
658+
659+ if (process != PROCESS_SECURITY) {
660+ /* Set resource limits for the process, skipping over any that
661+ * aren't set in the job class such that they inherit from
662+ * ourselves (and we inherit from kernel defaults).
663+ */
664+ for (i = 0; i < RLIMIT_NLIMITS; i++) {
665+ if (! class->limits[i])
666+ continue;
667+
668+ if (setrlimit (i, class->limits[i]) < 0) {
669+ nih_error_raise_system ();
670+ job_process_error_abort (fds[1],
671+ JOB_PROCESS_ERROR_RLIMIT, i);
672+ }
673+ }
674+
675+ /* Set the file mode creation mask; this is one of the few operations
676+ * that can never fail.
677+ */
678+ umask (class->umask);
679+
680+ /* Adjust the process priority ("nice level").
681+ */
682+ if (class->nice != JOB_NICE_INVALID &&
683+ setpriority (PRIO_PROCESS, 0, class->nice) < 0) {
684 nih_error_raise_system ();
685 job_process_error_abort (fds[1],
686- JOB_PROCESS_ERROR_RLIMIT, i);
687+ JOB_PROCESS_ERROR_PRIORITY, 0);
688 }
689- }
690-
691- /* Set the file mode creation mask; this is one of the few operations
692- * that can never fail.
693- */
694- umask (class->umask);
695-
696- /* Adjust the process priority ("nice level").
697- */
698- if (class->nice != JOB_NICE_INVALID &&
699- setpriority (PRIO_PROCESS, 0, class->nice) < 0) {
700- nih_error_raise_system ();
701- job_process_error_abort (fds[1],
702- JOB_PROCESS_ERROR_PRIORITY, 0);
703- }
704-
705- /* Adjust the process OOM killer priority.
706- */
707- if (class->oom_score_adj != JOB_DEFAULT_OOM_SCORE_ADJ) {
708- int oom_value;
709- snprintf (filename, sizeof (filename),
710- "/proc/%d/oom_score_adj", getpid ());
711- oom_value = class->oom_score_adj;
712- fd = fopen (filename, "w");
713- if ((! fd) && (errno == ENOENT)) {
714+
715+ /* Adjust the process OOM killer priority.
716+ */
717+ if (class->oom_score_adj != JOB_DEFAULT_OOM_SCORE_ADJ) {
718+ int oom_value;
719 snprintf (filename, sizeof (filename),
720- "/proc/%d/oom_adj", getpid ());
721- oom_value = (class->oom_score_adj
722- * ((class->oom_score_adj < 0) ? 17 : 15)) / 1000;
723+ "/proc/%d/oom_score_adj", getpid ());
724+ oom_value = class->oom_score_adj;
725 fd = fopen (filename, "w");
726- }
727- if (! fd) {
728- nih_error_raise_system ();
729- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OOM_ADJ, 0);
730- } else {
731- fprintf (fd, "%d\n", oom_value);
732-
733- if (fclose (fd)) {
734+ if ((! fd) && (errno == ENOENT)) {
735+ snprintf (filename, sizeof (filename),
736+ "/proc/%d/oom_adj", getpid ());
737+ oom_value = (class->oom_score_adj
738+ * ((class->oom_score_adj < 0) ? 17 : 15)) / 1000;
739+ fd = fopen (filename, "w");
740+ }
741+ if (! fd) {
742 nih_error_raise_system ();
743 job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OOM_ADJ, 0);
744- }
745- }
746- }
747-
748- /* Handle changing a chroot session job prior to dealing with
749- * the 'chroot' stanza.
750- */
751- if (class->session && class->session->chroot) {
752- if (chroot (class->session->chroot) < 0) {
753- nih_error_raise_system ();
754- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHROOT, 0);
755- }
756- }
757-
758- /* Change the root directory, confining path resolution within it;
759- * we do this before the working directory call so that is always
760- * relative to the new root.
761- */
762- if (class->chroot) {
763- if (chroot (class->chroot) < 0) {
764- nih_error_raise_system ();
765- job_process_error_abort (fds[1],
766- JOB_PROCESS_ERROR_CHROOT, 0);
767- }
768- }
769-
770- /* Change the working directory of the process, either to the one
771- * configured in the job, or to the root directory of the filesystem
772- * (or at least relative to the chroot).
773- */
774- if (class->chdir || user_mode == FALSE) {
775- if (chdir (class->chdir ? class->chdir : "/") < 0) {
776- nih_error_raise_system ();
777- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHDIR, 0);
778- }
779- }
780-
781- /* Change the user and group of the process to the one
782- * configured in the job. We must wait until now to lookup the
783- * UID and GID from the names to accommodate both chroot
784- * session jobs and jobs with a chroot stanza.
785- */
786- if (class->setuid) {
787- /* Without resetting errno, it's impossible to
788- * distinguish between a non-existent user and and
789- * error during lookup */
790- errno = 0;
791- pwd = getpwnam (class->setuid);
792- if (! pwd) {
793- if (errno != 0) {
794- nih_error_raise_system ();
795- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWNAM, 0);
796- } else {
797- nih_error_raise (JOB_PROCESS_INVALID_SETUID,
798- JOB_PROCESS_INVALID_SETUID_STR);
799- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_BAD_SETUID, 0);
800- }
801- }
802-
803- job_setuid = pwd->pw_uid;
804- /* This will be overridden if setgid is also set: */
805- job_setgid = pwd->pw_gid;
806- }
807-
808- if (class->setgid) {
809- errno = 0;
810- grp = getgrnam (class->setgid);
811- if (! grp) {
812- if (errno != 0) {
813- nih_error_raise_system ();
814- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRNAM, 0);
815- } else {
816- nih_error_raise (JOB_PROCESS_INVALID_SETGID,
817- JOB_PROCESS_INVALID_SETGID_STR);
818- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_BAD_SETGID, 0);
819- }
820- }
821-
822- job_setgid = grp->gr_gid;
823- }
824-
825- if (script_fd != -1 &&
826- (job_setuid != (uid_t) -1 || job_setgid != (gid_t) -1) &&
827- fchown (script_fd, job_setuid, job_setgid) < 0) {
828- nih_error_raise_system ();
829- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHOWN, 0);
830- }
831-
832- /* Make sure we always have the needed pwd and grp structs.
833- * Then pass those to initgroups() to setup the user's group list.
834- * Only do that if we're root as initgroups() won't work when non-root. */
835- if (geteuid () == 0) {
836- if (! pwd) {
837- pwd = getpwuid (geteuid ());
838- if (! pwd) {
839- nih_error_raise_system ();
840- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWUID, 0);
841- }
842- }
843-
844- if (! grp) {
845- grp = getgrgid (getegid ());
846- if (! grp) {
847- nih_error_raise_system ();
848- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRGID, 0);
849- }
850- }
851-
852- if (pwd && grp) {
853- if (initgroups (pwd->pw_name, grp->gr_gid) < 0) {
854- nih_error_raise_system ();
855- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_INITGROUPS, 0);
856- }
857- }
858- }
859-
860- /* Start dropping privileges */
861- if (job_setgid != (gid_t) -1 && setgid (job_setgid) < 0) {
862- nih_error_raise_system ();
863- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETGID, 0);
864- }
865-
866- if (job_setuid != (uid_t)-1 && setuid (job_setuid) < 0) {
867- nih_error_raise_system ();
868- job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETUID, 0);
869+ } else {
870+ fprintf (fd, "%d\n", oom_value);
871+
872+ if (fclose (fd)) {
873+ nih_error_raise_system ();
874+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_OOM_ADJ, 0);
875+ }
876+ }
877+ }
878+
879+ /* Handle changing a chroot session job prior to dealing with
880+ * the 'chroot' stanza.
881+ */
882+ if (class->session && class->session->chroot) {
883+ if (chroot (class->session->chroot) < 0) {
884+ nih_error_raise_system ();
885+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHROOT, 0);
886+ }
887+ }
888+
889+ /* Change the root directory, confining path resolution within it;
890+ * we do this before the working directory call so that is always
891+ * relative to the new root.
892+ */
893+ if (class->chroot) {
894+ if (chroot (class->chroot) < 0) {
895+ nih_error_raise_system ();
896+ job_process_error_abort (fds[1],
897+ JOB_PROCESS_ERROR_CHROOT, 0);
898+ }
899+ }
900+
901+ /* Change the working directory of the process, either to the one
902+ * configured in the job, or to the root directory of the filesystem
903+ * (or at least relative to the chroot).
904+ */
905+ if (class->chdir || user_mode == FALSE) {
906+ if (chdir (class->chdir ? class->chdir : "/") < 0) {
907+ nih_error_raise_system ();
908+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHDIR, 0);
909+ }
910+ }
911+
912+ /* Change the user and group of the process to the one
913+ * configured in the job. We must wait until now to lookup the
914+ * UID and GID from the names to accommodate both chroot
915+ * session jobs and jobs with a chroot stanza.
916+ */
917+ if (class->setuid) {
918+ /* Without resetting errno, it's impossible to
919+ * distinguish between a non-existent user and and
920+ * error during lookup */
921+ errno = 0;
922+ pwd = getpwnam (class->setuid);
923+ if (! pwd) {
924+ if (errno != 0) {
925+ nih_error_raise_system ();
926+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWNAM, 0);
927+ } else {
928+ nih_error_raise (JOB_PROCESS_INVALID_SETUID,
929+ JOB_PROCESS_INVALID_SETUID_STR);
930+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_BAD_SETUID, 0);
931+ }
932+ }
933+
934+ job_setuid = pwd->pw_uid;
935+ /* This will be overridden if setgid is also set: */
936+ job_setgid = pwd->pw_gid;
937+ }
938+
939+ if (class->setgid) {
940+ errno = 0;
941+ grp = getgrnam (class->setgid);
942+ if (! grp) {
943+ if (errno != 0) {
944+ nih_error_raise_system ();
945+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRNAM, 0);
946+ } else {
947+ nih_error_raise (JOB_PROCESS_INVALID_SETGID,
948+ JOB_PROCESS_INVALID_SETGID_STR);
949+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_BAD_SETGID, 0);
950+ }
951+ }
952+
953+ job_setgid = grp->gr_gid;
954+ }
955+
956+ if (script_fd != -1 &&
957+ (job_setuid != (uid_t) -1 || job_setgid != (gid_t) -1) &&
958+ fchown (script_fd, job_setuid, job_setgid) < 0) {
959+ nih_error_raise_system ();
960+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_CHOWN, 0);
961+ }
962+
963+ /* Make sure we always have the needed pwd and grp structs.
964+ * Then pass those to initgroups() to setup the user's group list.
965+ * Only do that if we're root as initgroups() won't work when non-root. */
966+ if (geteuid () == 0) {
967+ if (! pwd) {
968+ pwd = getpwuid (geteuid ());
969+ if (! pwd) {
970+ nih_error_raise_system ();
971+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETPWUID, 0);
972+ }
973+ }
974+
975+ if (! grp) {
976+ grp = getgrgid (getegid ());
977+ if (! grp) {
978+ nih_error_raise_system ();
979+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_GETGRGID, 0);
980+ }
981+ }
982+
983+ if (pwd && grp) {
984+ if (initgroups (pwd->pw_name, grp->gr_gid) < 0) {
985+ nih_error_raise_system ();
986+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_INITGROUPS, 0);
987+ }
988+ }
989+ }
990+
991+ /* Start dropping privileges */
992+ if (job_setgid != (gid_t) -1 && setgid (job_setgid) < 0) {
993+ nih_error_raise_system ();
994+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETGID, 0);
995+ }
996+
997+ if (job_setuid != (uid_t)-1 && setuid (job_setuid) < 0) {
998+ nih_error_raise_system ();
999+ job_process_error_abort (fds[1], JOB_PROCESS_ERROR_SETUID, 0);
1000+ }
1001 }
1002
1003 /* Reset all the signal handlers back to their default handling so
1004@@ -923,6 +948,7 @@
1005 nih_assert_not_reached ();
1006 }
1007
1008+
1009 /**
1010 * job_process_error_abort:
1011 * @fd: writing end of pipe,
1012@@ -1185,6 +1211,11 @@
1013 err, _("unable to initgroups: %s"),
1014 strerror (err->errnum)));
1015 break;
1016+ case JOB_PROCESS_ERROR_SECURITY:
1017+ err->error.message = NIH_MUST (nih_sprintf (
1018+ err, _("unable to switch security profile: %s"),
1019+ strerror (err->errnum)));
1020+ break;
1021 default:
1022 nih_assert_not_reached ();
1023 }
1024@@ -1640,6 +1671,17 @@
1025 */
1026 stop = TRUE;
1027 break;
1028+ case PROCESS_SECURITY:
1029+ nih_assert (job->state == JOB_SECURITY);
1030+
1031+ /* We should always fail the job if the security profile
1032+ * failed to load
1033+ */
1034+ if (status) {
1035+ failed = TRUE;
1036+ stop = TRUE;
1037+ }
1038+ break;
1039 case PROCESS_PRE_START:
1040 nih_assert (job->state == JOB_PRE_START);
1041
1042
1043=== modified file 'init/job_process.h'
1044--- init/job_process.h 2013-03-28 17:07:54 +0000
1045+++ init/job_process.h 2013-05-27 14:31:27 +0000
1046@@ -94,7 +94,8 @@
1047 JOB_PROCESS_ERROR_SIGNAL,
1048 JOB_PROCESS_ERROR_ALLOC,
1049 JOB_PROCESS_ERROR_INITGROUPS,
1050- JOB_PROCESS_ERROR_GETGRGID
1051+ JOB_PROCESS_ERROR_GETGRGID,
1052+ JOB_PROCESS_ERROR_SECURITY
1053 } JobProcessErrorType;
1054
1055 /**
1056
1057=== modified file 'init/man/init.5'
1058--- init/man/init.5 2013-03-25 12:55:08 +0000
1059+++ init/man/init.5 2013-05-27 14:31:27 +0000
1060@@ -916,6 +916,37 @@
1061 .P
1062
1063 .\"
1064+.SS AppArmor support
1065+Upstart provides several stanzas for loading and switching to different
1066+AppArmor profiles. If AppArmor isn't enabled in the currently running
1067+kernel, the stanzas will be silently ignored.
1068+
1069+.TP
1070+.B apparmor load \fIPROFILE
1071+This stanza specifies an AppArmor profile to load into the Linux kernel at
1072+job start. The AppArmor profile will confine a main process automatically
1073+using path attachment, or manually by using the \fBapparmor switch\fP
1074+stanza.
1075+.I PROFILE
1076+must be an absolute path to a profile and a failure will occur if the file
1077+doesn't exist.
1078+
1079+.nf
1080+apparmor load /etc/apparmor.d/usr.sbin.cupsd
1081+.fi
1082+.\"
1083+.TP
1084+.B apparmor switch \fINAME
1085+This stanza specifies the name of an AppArmor profile name to switch to
1086+before running the main process.
1087+.I NAME
1088+must be the name of a profile already loaded into the running Linux kernel,
1089+and will result in a failure if not available.
1090+
1091+.nf
1092+apparmor switch /usr/sbin/cupsd
1093+.fi
1094+.\"
1095 .SS Miscellaneous
1096 .TP
1097 .B kill signal \fISIGNAL
1098@@ -1073,3 +1104,4 @@
1099 .BR prctl (2)
1100 .BR pty (7)
1101 .BR sh (1)
1102+.BR apparmor (7)
1103
1104=== modified file 'init/parse_job.c'
1105--- init/parse_job.c 2013-02-27 11:46:04 +0000
1106+++ init/parse_job.c 2013-05-27 14:31:27 +0000
1107@@ -45,6 +45,7 @@
1108 #include "event.h"
1109 #include "parse_job.h"
1110 #include "errors.h"
1111+#include "apparmor.h"
1112
1113
1114 /* Prototypes for static functions */
1115@@ -170,6 +171,11 @@
1116 size_t *pos, size_t *lineno)
1117 __attribute__ ((warn_unused_result));
1118
1119+static int stanza_apparmor (JobClass *class, NihConfigStanza *stanza,
1120+ const char *file, size_t len,
1121+ size_t *pos, size_t *lineno)
1122+ __attribute__ ((warn_unused_result));
1123+
1124 static int stanza_respawn (JobClass *class, NihConfigStanza *stanza,
1125 const char *file, size_t len,
1126 size_t *pos, size_t *lineno)
1127@@ -270,6 +276,7 @@
1128 { "debug", (NihConfigHandler)stanza_debug },
1129 { "manual", (NihConfigHandler)stanza_manual },
1130 { "usage", (NihConfigHandler)stanza_usage },
1131+ { "apparmor", (NihConfigHandler)stanza_apparmor },
1132
1133 NIH_CONFIG_LAST
1134 };
1135@@ -1931,6 +1938,108 @@
1136 return ret;
1137 }
1138
1139+/**
1140+ * stanza_apparmor:
1141+ * @class: job class being parsed,
1142+ * @stanza: stanza found,
1143+ * @file: file or string to parse,
1144+ * @len: length of @file,
1145+ * @pos: offset within @file,
1146+ * @lineno: line number.
1147+ *
1148+ * Parse an apparmor stanza from @file, extracting a second-level stanza that
1149+ * states which value to set from its argument.
1150+ *
1151+ * Returns: zero on success, negative value on error.
1152+ **/
1153+static int
1154+stanza_apparmor (JobClass *class,
1155+ NihConfigStanza *stanza,
1156+ const char *file,
1157+ size_t len,
1158+ size_t *pos,
1159+ size_t *lineno)
1160+{
1161+ size_t a_pos, a_lineno;
1162+ int ret = -1;
1163+ nih_local char *arg = NULL;
1164+ Process *process;
1165+
1166+ nih_assert (class != NULL);
1167+ nih_assert (stanza != NULL);
1168+ nih_assert (file != NULL);
1169+ nih_assert (pos != NULL);
1170+
1171+ a_pos = *pos;
1172+ a_lineno = (lineno ? *lineno : 1);
1173+
1174+ arg = nih_config_next_token (NULL, file, len, &a_pos, &a_lineno,
1175+ NIH_CONFIG_CNLWS, FALSE);
1176+ if (! arg)
1177+ goto finish;
1178+
1179+ if (! strcmp (arg, "load")) {
1180+ nih_local char *aaarg = NULL;
1181+
1182+ /* Update error position to the load value */
1183+ *pos = a_pos;
1184+ if (lineno)
1185+ *lineno = a_lineno;
1186+
1187+ aaarg = nih_config_next_arg (NULL, file, len,
1188+ &a_pos, &a_lineno);
1189+
1190+ if (! aaarg)
1191+ goto finish;
1192+
1193+ /* Allocate a new Process structure if we need to */
1194+ if (! class->process[PROCESS_SECURITY]) {
1195+ class->process[PROCESS_SECURITY] = process_new (class->process);
1196+ if (! class->process[PROCESS_SECURITY])
1197+ nih_return_system_error (-1);
1198+ }
1199+
1200+ process = class->process[PROCESS_SECURITY];
1201+
1202+ if (process->command)
1203+ nih_unref (process->command, process);
1204+
1205+ process->script = FALSE;
1206+ process->command = nih_sprintf (process, "%s %s %s",
1207+ APPARMOR_PARSER,
1208+ APPARMOR_PARSER_OPTS,
1209+ aaarg);
1210+
1211+ if (! process->command)
1212+ nih_return_system_error (-1);
1213+
1214+ } else if (! strcmp (arg, "switch")) {
1215+ /* Update error position to the switch value */
1216+ *pos = a_pos;
1217+ if (lineno)
1218+ *lineno = a_lineno;
1219+
1220+ class->apparmor_switch = nih_config_next_arg (class, file,
1221+ len, &a_pos,
1222+ &a_lineno);
1223+
1224+ if (! class->apparmor_switch)
1225+ goto finish;
1226+
1227+ } else {
1228+ nih_return_error (-1, NIH_CONFIG_UNKNOWN_STANZA,
1229+ _(NIH_CONFIG_UNKNOWN_STANZA_STR));
1230+ }
1231+
1232+ ret = nih_config_skip_comment (file, len, &a_pos, &a_lineno);
1233+
1234+finish:
1235+ *pos = a_pos;
1236+ if (lineno)
1237+ *lineno = a_lineno;
1238+
1239+ return ret;
1240+}
1241
1242 /**
1243 * stanza_respawn:
1244
1245=== modified file 'init/process.c'
1246--- init/process.c 2012-11-14 14:47:19 +0000
1247+++ init/process.c 2013-05-27 14:31:27 +0000
1248@@ -86,6 +86,8 @@
1249 return N_("pre-stop");
1250 case PROCESS_POST_STOP:
1251 return N_("post-stop");
1252+ case PROCESS_SECURITY:
1253+ return N_("security");
1254 default:
1255 return NULL;
1256 }
1257@@ -114,6 +116,8 @@
1258 return PROCESS_PRE_STOP;
1259 } else if (! strcmp (process, "post-stop")) {
1260 return PROCESS_POST_STOP;
1261+ } else if (! strcmp (process, "security")) {
1262+ return PROCESS_SECURITY;
1263 } else {
1264 return -1;
1265 }
1266@@ -311,6 +315,7 @@
1267 state_enum_to_str (PROCESS_POST_START, type);
1268 state_enum_to_str (PROCESS_PRE_STOP, type);
1269 state_enum_to_str (PROCESS_POST_STOP, type);
1270+ state_enum_to_str (PROCESS_SECURITY, type);
1271
1272 return NULL;
1273 }
1274@@ -335,6 +340,7 @@
1275 state_str_to_enum (PROCESS_POST_START, type);
1276 state_str_to_enum (PROCESS_PRE_STOP, type);
1277 state_str_to_enum (PROCESS_POST_STOP, type);
1278+ state_str_to_enum (PROCESS_SECURITY, type);
1279
1280 return -1;
1281 }
1282
1283=== modified file 'init/process.h'
1284--- init/process.h 2013-02-27 11:46:04 +0000
1285+++ init/process.h 2013-05-27 14:31:27 +0000
1286@@ -45,6 +45,7 @@
1287 PROCESS_POST_START,
1288 PROCESS_PRE_STOP,
1289 PROCESS_POST_STOP,
1290+ PROCESS_SECURITY,
1291 PROCESS_LAST,
1292 } ProcessType;
1293
1294
1295=== added file 'init/tests/data/upstart-pre-security.json'
1296--- init/tests/data/upstart-pre-security.json 1970-01-01 00:00:00 +0000
1297+++ init/tests/data/upstart-pre-security.json 2013-05-27 14:31:27 +0000
1298@@ -0,0 +1,1 @@
1299+{ "sessions": [ ], "events": [ { "session": 0, "name": "Christmas", "env": [ "JOB=udevtrigger", "INSTANCE=", "RESULT=ok" ], "fd": -1, "progress": "EVENT_HANDLING", "failed": 1, "blockers": 1 } ], "job_classes": [ { "session": 0, "name": "security", "path": "\/com\/ubuntu\/Upstart\/jobs\/security", "instance": "", "jobs": [ { "name": "", "path": "\/com\/ubuntu\/Upstart\/jobs\/security\/_", "goal": "JOB_START", "state": "JOB_RUNNING", "env": [ ], "start_env": [ ], "stop_env": [ ], "stop_on": "runlevel [06]", "fds": [ ], "pid": [ 10, 11, 12, 13, 14 ], "kill_process": "PROCESS_INVALID", "failed": 0, "failed_process": "PROCESS_INVALID", "exit_status": 0, "respawn_time": 0, "respawn_count": 0, "trace_forks": 1, "trace_state": "TRACE_NONE", "log": [ { "path": null }, { "path": null }, { "path": null }, { "path": null }, { "path": null } ] } ], "description": null, "author": null, "version": null, "env": [ ], "export": [ ], "start_on": "virtual-filesystems", "stop_on": "runlevel [06]", "emits": [ ], "process": [ { "script": 0, "command": "a" }, { "script": 0, "command": "b" }, { "script": 0, "command": "c" }, { "script": 0, "command": "d" }, { "script": 0, "command": "e" } ], "expect": "EXPECT_FORK", "task": 0, "kill_timeout": 5, "kill_signal": 15, "respawn": 1, "respawn_limit": 10, "respawn_interval": 5, "normalexit": [ ], "console": "CONSOLE_LOG", "umask": 18, "nice": -21, "oom_score_adj": 0, "limits": [ { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 }, { "rlim_cur": 0, "rlim_max": 0 } ], "chroot": null, "chdir": null, "setuid": null, "setgid": null, "deleted": 0, "debug": 0, "usage": null } ] }
1300
1301=== modified file 'init/tests/test_job.c'
1302--- init/tests/test_job.c 2012-09-13 16:20:10 +0000
1303+++ init/tests/test_job.c 2013-05-27 14:31:27 +0000
1304@@ -1047,7 +1047,7 @@
1305 }
1306
1307 job->goal = JOB_START;
1308- job->state = JOB_STARTING;
1309+ job->state = JOB_SECURITY;
1310 job->pid[PROCESS_PRE_START] = 0;
1311
1312 job->blocker = NULL;
1313@@ -1113,7 +1113,7 @@
1314 }
1315
1316 job->goal = JOB_START;
1317- job->state = JOB_STARTING;
1318+ job->state = JOB_SECURITY;
1319 job->pid[PROCESS_MAIN] = 0;
1320
1321 job->blocker = NULL;
1322@@ -1187,7 +1187,7 @@
1323 }
1324
1325 job->goal = JOB_START;
1326- job->state = JOB_STARTING;
1327+ job->state = JOB_SECURITY;
1328 job->pid[PROCESS_PRE_START] = 0;
1329
1330 job->blocker = NULL;
1331@@ -4000,14 +4000,31 @@
1332
1333
1334 /* Check that the next state if we're starting a starting job is
1335+ * security.
1336+ */
1337+ TEST_FEATURE ("with starting job and a goal of start");
1338+ job->goal = JOB_START;
1339+ job->state = JOB_STARTING;
1340+
1341+ TEST_EQ (job_next_state (job), JOB_SECURITY);
1342+
1343+ /* Check that the next state if we're starting a security job is
1344 * pre-start.
1345 */
1346- TEST_FEATURE ("with starting job and a goal of start");
1347+ TEST_FEATURE ("with security job and a goal of start");
1348 job->goal = JOB_START;
1349- job->state = JOB_STARTING;
1350+ job->state = JOB_SECURITY;
1351
1352 TEST_EQ (job_next_state (job), JOB_PRE_START);
1353
1354+ /* Check that the next state if we're stopping an security job is
1355+ * stopping.
1356+ */
1357+ TEST_FEATURE ("with security job and a goal of stop");
1358+ job->goal = JOB_STOP;
1359+ job->state = JOB_SECURITY;
1360+
1361+ TEST_EQ (job_next_state (job), JOB_STOPPING);
1362
1363 /* Check that the next state if we're stopping a pre-start job is
1364 * stopping.
1365@@ -5695,6 +5712,13 @@
1366 TEST_EQ_STR (name, "starting");
1367
1368
1369+ /* Check that the JOB_SECURITY state returns the right string. */
1370+ TEST_FEATURE ("with security state");
1371+ name = job_state_name (JOB_SECURITY);
1372+
1373+ TEST_EQ_STR (name, "security");
1374+
1375+
1376 /* Check that the JOB_PRE_START state returns the right string. */
1377 TEST_FEATURE ("with pre-start state");
1378 name = job_state_name (JOB_PRE_START);
1379@@ -5779,6 +5803,13 @@
1380 TEST_EQ (state, JOB_STARTING);
1381
1382
1383+ /* Check that JOB_SECURITY is returned for the right string. */
1384+ TEST_FEATURE ("with security state");
1385+ state = job_state_from_name ("security");
1386+
1387+ TEST_EQ (state, JOB_SECURITY);
1388+
1389+
1390 /* Check that JOB_PRE_START is returned for the right string. */
1391 TEST_FEATURE ("with pre-start state");
1392 state = job_state_from_name ("pre-start");
1393
1394=== modified file 'init/tests/test_job_class.c'
1395--- init/tests/test_job_class.c 2013-01-10 17:01:56 +0000
1396+++ init/tests/test_job_class.c 2013-05-27 14:31:27 +0000
1397@@ -145,6 +145,8 @@
1398 TEST_EQ_P (class->setuid, NULL);
1399 TEST_EQ_P (class->setgid, NULL);
1400
1401+ TEST_EQ_P (class->apparmor_switch, NULL);
1402+
1403 TEST_FALSE (class->deleted);
1404
1405 nih_free (class);
1406
1407=== modified file 'init/tests/test_parse_job.c'
1408--- init/tests/test_parse_job.c 2012-10-22 13:29:45 +0000
1409+++ init/tests/test_parse_job.c 2013-05-27 14:31:27 +0000
1410@@ -36,6 +36,7 @@
1411 #include "conf.h"
1412 #include "parse_job.h"
1413 #include "errors.h"
1414+#include "apparmor.h"
1415
1416
1417 void
1418@@ -467,6 +468,267 @@
1419 }
1420
1421 void
1422+test_stanza_apparmor (void)
1423+{
1424+ JobClass *job;
1425+ Process *process;
1426+ NihError *err;
1427+ size_t pos, lineno;
1428+ char buf[1024];
1429+
1430+ TEST_FUNCTION ("stanza_apparmor");
1431+
1432+
1433+ /* Check that an apparmor load stanza sets the process of the
1434+ * job as a single string.
1435+ */
1436+ TEST_FEATURE ("with load and profile");
1437+ strcpy (buf, "apparmor load /etc/apparmor.d/usr.sbin.cupsd\n");
1438+
1439+ /* TODO: investigate why we can't use TEST_ALLOC_FAIL here.
1440+ * It fails when nih_sprintf() is used.
1441+ */
1442+
1443+ pos = 0;
1444+ lineno = 1;
1445+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf),
1446+ &pos, &lineno);
1447+
1448+ TEST_EQ (pos, strlen (buf));
1449+ TEST_EQ (lineno, 2);
1450+
1451+ TEST_ALLOC_SIZE (job, sizeof (JobClass));
1452+
1453+ process = job->process[PROCESS_SECURITY];
1454+ TEST_ALLOC_PARENT (process, job->process);
1455+ TEST_ALLOC_SIZE (process, sizeof (Process));
1456+ TEST_EQ (process->script, FALSE);
1457+ TEST_ALLOC_PARENT (process->command, process);
1458+ strcpy (buf, APPARMOR_PARSER);
1459+ strcat (buf, " ");
1460+ strcat (buf, APPARMOR_PARSER_OPTS);
1461+ strcat (buf, " /etc/apparmor.d/usr.sbin.cupsd");
1462+ TEST_EQ_STR (process->command, buf);
1463+
1464+ nih_free (job);
1465+
1466+
1467+ /* Check that the last of multiple apparmor load stanzas is used. */
1468+ TEST_FEATURE ("with multiple load");
1469+ strcpy (buf, "apparmor load /etc/apparmor.d/usr.sbin.rsyslogd\n");
1470+ strcat (buf, "apparmor load /etc/apparmor.d/usr.sbin.cupsd\n");
1471+
1472+ /* TODO: investigate why we can't use TEST_ALLOC_FAIL here.
1473+ * It fails when nih_sprintf() is used.
1474+ */
1475+
1476+ pos = 0;
1477+ lineno = 1;
1478+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf),
1479+ &pos, &lineno);
1480+
1481+ TEST_EQ (pos, strlen (buf));
1482+ TEST_EQ (lineno, 3);
1483+
1484+ TEST_ALLOC_SIZE (job, sizeof (JobClass));
1485+
1486+ process = job->process[PROCESS_SECURITY];
1487+ TEST_ALLOC_PARENT (process, job->process);
1488+ TEST_ALLOC_SIZE (process, sizeof (Process));
1489+ TEST_EQ (process->script, FALSE);
1490+ TEST_ALLOC_PARENT (process->command, process);
1491+ strcpy (buf, APPARMOR_PARSER);
1492+ strcat (buf, " ");
1493+ strcat (buf, APPARMOR_PARSER_OPTS);
1494+ strcat (buf, " /etc/apparmor.d/usr.sbin.cupsd");
1495+ TEST_EQ_STR (process->command, buf);
1496+
1497+ nih_free (job);
1498+
1499+
1500+ /* Check that an apparmor load stanza without any arguments results
1501+ * in a syntax error.
1502+ */
1503+ TEST_FEATURE ("with load but no profile");
1504+ strcpy (buf, "apparmor load\n");
1505+
1506+ pos = 0;
1507+ lineno = 1;
1508+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
1509+
1510+ TEST_EQ_P (job, NULL);
1511+
1512+ err = nih_error_get ();
1513+ TEST_EQ (err->number, NIH_CONFIG_EXPECTED_TOKEN);
1514+ TEST_EQ (pos, 13);
1515+ TEST_EQ (lineno, 1);
1516+ nih_free (err);
1517+
1518+
1519+ /* Check that an apparmor load stanza with an extra argument
1520+ * results in a syntax error.
1521+ */
1522+ TEST_FEATURE ("with extra argument to load");
1523+ strcpy (buf, "apparmor load /etc/apparmor.d/usr.sbin.cupsd extra\n");
1524+
1525+ pos = 0;
1526+ lineno = 1;
1527+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
1528+
1529+ TEST_EQ_P (job, NULL);
1530+
1531+ err = nih_error_get ();
1532+ TEST_EQ (err->number, NIH_CONFIG_UNEXPECTED_TOKEN);
1533+ TEST_EQ (pos, 45);
1534+ TEST_EQ (lineno, 1);
1535+ nih_free (err);
1536+
1537+
1538+ /* Check that an apparmor stanza with an unknown second argument
1539+ * results in a syntax error.
1540+ */
1541+ TEST_FEATURE ("with unknown argument");
1542+ strcpy (buf, "apparmor foo\n");
1543+
1544+ pos = 0;
1545+ lineno = 1;
1546+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
1547+
1548+ TEST_EQ_P (job, NULL);
1549+
1550+ err = nih_error_get ();
1551+ TEST_EQ (err->number, NIH_CONFIG_UNKNOWN_STANZA);
1552+ TEST_EQ (pos, 9);
1553+ TEST_EQ (lineno, 1);
1554+ nih_free (err);
1555+
1556+
1557+ /* Check that an apparmor stanza with no second argument
1558+ * results in a syntax error.
1559+ */
1560+ TEST_FEATURE ("with missing argument");
1561+ strcpy (buf, "apparmor\n");
1562+
1563+ pos = 0;
1564+ lineno = 1;
1565+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
1566+
1567+ TEST_EQ_P (job, NULL);
1568+
1569+ err = nih_error_get ();
1570+ TEST_EQ (err->number, NIH_CONFIG_EXPECTED_TOKEN);
1571+ TEST_EQ (pos, 8);
1572+ TEST_EQ (lineno, 1);
1573+ nih_free (err);
1574+
1575+
1576+ /* Check that an apparmor switch stanza results in it
1577+ * being stored in the job.
1578+ */
1579+ TEST_FEATURE ("with switch and profile");
1580+ strcpy (buf, "apparmor switch /usr/sbin/cupsd\n");
1581+
1582+ TEST_ALLOC_FAIL {
1583+ pos = 0;
1584+ lineno = 1;
1585+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf),
1586+ &pos, &lineno);
1587+
1588+ if (test_alloc_failed) {
1589+ TEST_EQ_P (job, NULL);
1590+
1591+ err = nih_error_get ();
1592+ TEST_EQ (err->number, ENOMEM);
1593+ nih_free (err);
1594+
1595+ continue;
1596+ }
1597+
1598+ TEST_EQ (pos, strlen (buf));
1599+ TEST_EQ (lineno, 2);
1600+
1601+ TEST_ALLOC_SIZE (job, sizeof (JobClass));
1602+
1603+ TEST_ALLOC_PARENT (job->apparmor_switch, job);
1604+ TEST_EQ_STR (job->apparmor_switch, "/usr/sbin/cupsd");
1605+
1606+ nih_free (job);
1607+ }
1608+
1609+
1610+ /* Check that the last of multiple apparmor switch stanzas is used. */
1611+ TEST_FEATURE ("with multiple apparmor switch stanzas");
1612+ strcpy (buf, "apparmor switch /usr/sbin/rsyslogd\n");
1613+ strcat (buf, "apparmor switch /usr/sbin/cupsd\n");
1614+
1615+ TEST_ALLOC_FAIL {
1616+ pos = 0;
1617+ lineno = 1;
1618+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf),
1619+ &pos, &lineno);
1620+
1621+ if (test_alloc_failed) {
1622+ TEST_EQ_P (job, NULL);
1623+
1624+ err = nih_error_get ();
1625+ TEST_EQ (err->number, ENOMEM);
1626+ nih_free (err);
1627+
1628+ continue;
1629+ }
1630+
1631+ TEST_EQ (pos, strlen (buf));
1632+ TEST_EQ (lineno, 3);
1633+
1634+ TEST_ALLOC_SIZE (job, sizeof (JobClass));
1635+
1636+ TEST_ALLOC_PARENT (job->apparmor_switch, job);
1637+ TEST_EQ_STR (job->apparmor_switch, "/usr/sbin/cupsd");
1638+
1639+ nih_free (job);
1640+ }
1641+
1642+
1643+ /* Check that an apparmor switch stanza without a profile results in
1644+ * a syntax error.
1645+ */
1646+ TEST_FEATURE ("with switch and no profile");
1647+ strcpy (buf, "apparmor switch\n");
1648+
1649+ pos = 0;
1650+ lineno = 1;
1651+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
1652+
1653+ TEST_EQ_P (job, NULL);
1654+
1655+ err = nih_error_get ();
1656+ TEST_EQ (err->number, NIH_CONFIG_EXPECTED_TOKEN);
1657+ TEST_EQ (pos, 15);
1658+ TEST_EQ (lineno, 1);
1659+ nih_free (err);
1660+
1661+
1662+ /* Check that an apparmor switch stanza with an extra second argument
1663+ * results in a syntax error.
1664+ */
1665+ TEST_FEATURE ("with extra argument to switch");
1666+ strcpy (buf, "apparmor switch /usr/sbin/cupsd extra\n");
1667+
1668+ pos = 0;
1669+ lineno = 1;
1670+ job = parse_job (NULL, NULL, NULL, "test", buf, strlen (buf), &pos, &lineno);
1671+
1672+ TEST_EQ_P (job, NULL);
1673+
1674+ err = nih_error_get ();
1675+ TEST_EQ (err->number, NIH_CONFIG_UNEXPECTED_TOKEN);
1676+ TEST_EQ (pos, 32);
1677+ TEST_EQ (lineno, 1);
1678+ nih_free (err);
1679+
1680+}
1681+
1682+void
1683 test_stanza_pre_start (void)
1684 {
1685 JobClass *job;
1686@@ -8372,6 +8634,7 @@
1687
1688 test_stanza_exec ();
1689 test_stanza_script ();
1690+ test_stanza_apparmor ();
1691 test_stanza_pre_start ();
1692 test_stanza_post_start ();
1693 test_stanza_pre_stop ();
1694
1695=== modified file 'init/tests/test_process.c'
1696--- init/tests/test_process.c 2011-05-15 12:53:17 +0000
1697+++ init/tests/test_process.c 2013-05-27 14:31:27 +0000
1698@@ -68,6 +68,13 @@
1699 TEST_EQ_STR (name, "main");
1700
1701
1702+ /* Check that PROCESS_SECURITY returns the right string. */
1703+ TEST_FEATURE ("with security process");
1704+ name = process_name (PROCESS_SECURITY);
1705+
1706+ TEST_EQ_STR (name, "security");
1707+
1708+
1709 /* Check that PROCESS_PRE_START returns the right string. */
1710 TEST_FEATURE ("with pre-start process");
1711 name = process_name (PROCESS_PRE_START);
1712@@ -117,6 +124,13 @@
1713 TEST_EQ (process, PROCESS_MAIN);
1714
1715
1716+ /* Check that PROCESS_SECURITY is returned for the string. */
1717+ TEST_FEATURE ("with security process");
1718+ process = process_from_name ("security");
1719+
1720+ TEST_EQ (process, PROCESS_SECURITY);
1721+
1722+
1723 /* Check that PROCESS_PRE_START is returned for the string. */
1724 TEST_FEATURE ("with pre-start process");
1725 process = process_from_name ("pre-start");
1726
1727=== modified file 'init/tests/test_state.c'
1728--- init/tests/test_state.c 2013-02-27 11:46:04 +0000
1729+++ init/tests/test_state.c 2013-05-27 14:31:27 +0000
1730@@ -143,6 +143,7 @@
1731 __attribute__ ((warn_unused_result));
1732
1733 void test_upstart1_6_upgrade (const char *conf_file, const char *path);
1734+void test_upstart_pre_security_upgrade (const char *conf_file, const char *path);
1735
1736 /**
1737 * TestDataFile:
1738@@ -175,6 +176,7 @@
1739 **/
1740 TestDataFile test_data_files[] = {
1741 { "bar", "upstart-1.6.json", test_upstart1_6_upgrade },
1742+ { "security", "upstart-pre-security.json", test_upstart_pre_security_upgrade },
1743
1744 { NULL, NULL, NULL }
1745 };
1746@@ -615,6 +617,8 @@
1747 if (obj_string_check (a, b, usage))
1748 goto fail;
1749
1750+ if (obj_string_check (a, b, apparmor_switch))
1751+ goto fail;
1752
1753 return 0;
1754
1755@@ -1013,6 +1017,12 @@
1756 foo->process[PROCESS_MAIN]->command = NIH_MUST (nih_strdup (foo->process[PROCESS_MAIN],
1757 "echo hello !£$%^&*()_+-={}:@~;'#<>?,./"));
1758
1759+ foo->process[PROCESS_SECURITY] = process_new (foo->process);
1760+ TEST_NE_P (foo->process[PROCESS_SECURITY], NULL);
1761+ foo->process[PROCESS_SECURITY]->script = 0;
1762+ foo->process[PROCESS_SECURITY]->command = NIH_MUST (nih_strdup (foo->process[PROCESS_SECURITY],
1763+ "/bin/true"));
1764+
1765 foo->process[PROCESS_PRE_START] = process_new (foo->process);
1766 TEST_NE_P (foo->process[PROCESS_PRE_START], NULL);
1767 foo->process[PROCESS_PRE_START]->script = 0;
1768@@ -3068,6 +3078,131 @@
1769
1770 nih_free (event);
1771 nih_free (conf_sources);
1772+ conf_sources = NULL;
1773+ nih_free (job_classes);
1774+ job_classes = NULL;
1775+}
1776+
1777+/**
1778+ * test_upstart_pre_security_upgrade:
1779+ *
1780+ * @conf_file: name of ConfFile to create prior to running test,
1781+ * @path: full path to JSON data file to deserialise.
1782+ *
1783+ * Test for Upstart pre-security serialisation data format that doesn't
1784+ * contain apparmor_switch element, and PROCESS_SECURITY.
1785+ *
1786+ **/
1787+void
1788+test_upstart_pre_security_upgrade (const char *conf_file, const char *path)
1789+{
1790+ nih_local char *json_string = NULL;
1791+ Event *event;
1792+ ConfSource *source;
1793+ ConfFile *file;
1794+ nih_local char *conf_file_path = NULL;
1795+ struct stat statbuf;
1796+ size_t len;
1797+
1798+ nih_assert (conf_file);
1799+ nih_assert (path);
1800+
1801+ conf_init ();
1802+ session_init ();
1803+ event_init ();
1804+ control_init ();
1805+ job_class_init ();
1806+
1807+ TEST_LIST_EMPTY (sessions);
1808+ TEST_LIST_EMPTY (events);
1809+ TEST_LIST_EMPTY (conf_sources);
1810+ TEST_HASH_EMPTY (job_classes);
1811+
1812+ /* Check data file exists */
1813+ TEST_EQ (stat (path, &statbuf), 0);
1814+
1815+ json_string = nih_file_read (NULL, path, &len);
1816+ TEST_NE_P (json_string, NULL);
1817+
1818+ /* Create the ConfSource and ConfFile objects to simulate
1819+ * Upstart reading /etc/init on startup. Required since we
1820+ * don't currently serialise these objects.
1821+ */
1822+ source = conf_source_new (NULL, "/tmp/security", CONF_JOB_DIR);
1823+ TEST_NE_P (source, NULL);
1824+
1825+ conf_file_path = NIH_MUST (nih_sprintf (NULL, "%s/%s",
1826+ "/tmp/security", conf_file));
1827+
1828+ file = conf_file_new (source, conf_file_path);
1829+ TEST_NE_P (file, NULL);
1830+
1831+ /* Recreate state from JSON data file */
1832+ assert0 (state_from_string (json_string));
1833+
1834+ TEST_LIST_NOT_EMPTY (conf_sources);
1835+ TEST_LIST_NOT_EMPTY (events);
1836+ TEST_HASH_NOT_EMPTY (job_classes);
1837+ TEST_LIST_EMPTY (sessions);
1838+
1839+ event = (Event *)nih_list_remove (events->next);
1840+ TEST_NE_P (event, NULL);
1841+ TEST_EQ_STR (event->name, "Christmas");
1842+
1843+ NIH_HASH_FOREACH (job_classes, iter) {
1844+ JobClass *class = (JobClass *)iter;
1845+
1846+ TEST_EQ_STR (class->name, "security");
1847+ TEST_EQ_STR (class->path, "/com/ubuntu/Upstart/jobs/security");
1848+ TEST_EQ_P (class->apparmor_switch, NULL);
1849+ TEST_HASH_NOT_EMPTY (class->instances);
1850+
1851+ TEST_EQ_P (class->process[PROCESS_SECURITY], NULL);
1852+
1853+ TEST_FALSE (class->process[PROCESS_MAIN]->script);
1854+ TEST_FALSE (class->process[PROCESS_PRE_START]->script);
1855+ TEST_FALSE (class->process[PROCESS_POST_START]->script);
1856+ TEST_FALSE (class->process[PROCESS_PRE_STOP]->script);
1857+ TEST_FALSE (class->process[PROCESS_POST_STOP]->script);
1858+
1859+ TEST_EQ_STR (class->process[PROCESS_MAIN]->command, "a");
1860+ TEST_EQ_STR (class->process[PROCESS_PRE_START]->command, "b");
1861+ TEST_EQ_STR (class->process[PROCESS_POST_START]->command, "c");
1862+ TEST_EQ_STR (class->process[PROCESS_PRE_STOP]->command, "d");
1863+ TEST_EQ_STR (class->process[PROCESS_POST_STOP]->command, "e");
1864+
1865+ NIH_HASH_FOREACH (class->instances, iter2) {
1866+ Job *job = (Job *)iter2;
1867+ nih_local char *instance_path = NULL;
1868+
1869+ /* instance name */
1870+ TEST_EQ_STR (job->name, "");
1871+
1872+ instance_path = NIH_MUST (nih_sprintf (NULL, "%s/_", class->path));
1873+ TEST_EQ_STR (job->path, instance_path);
1874+
1875+ TEST_EQ (job->pid[PROCESS_MAIN], 10);
1876+ TEST_EQ (job->pid[PROCESS_PRE_START], 11);
1877+ TEST_EQ (job->pid[PROCESS_POST_START], 12);
1878+ TEST_EQ (job->pid[PROCESS_PRE_STOP], 13);
1879+ TEST_EQ (job->pid[PROCESS_POST_STOP], 14);
1880+ TEST_EQ (job->pid[PROCESS_SECURITY], 0);
1881+
1882+ TEST_EQ_P (job->log[PROCESS_MAIN], NULL);
1883+ TEST_EQ_P (job->log[PROCESS_PRE_START], NULL);
1884+ TEST_EQ_P (job->log[PROCESS_POST_START], NULL);
1885+ TEST_EQ_P (job->log[PROCESS_PRE_STOP], NULL);
1886+ TEST_EQ_P (job->log[PROCESS_POST_STOP], NULL);
1887+ TEST_EQ_P (job->log[PROCESS_SECURITY], NULL);
1888+
1889+ }
1890+ }
1891+
1892+ nih_free (event);
1893+ nih_free (conf_sources);
1894+ conf_sources = NULL;
1895+ nih_free (job_classes);
1896+ job_classes = NULL;
1897 }
1898
1899 int

Subscribers

People subscribed via source and target branches