1 /************************************************************************
2 *
3 * run_init
4 *
5 * SYNOPSIS:
6 *
7 * This program allows a user to run an /etc/init.d script in the proper context.
8 *
9 * USAGE:
10 *
11 * run_init <script> <args>
12 *
13 * BUILD OPTIONS:
14 *
15 * option USE_PAM:
16 *
17 * Set the USE_PAM constant if you want to authenticate users via PAM.
18 * If USE_PAM is not set, users will be authenticated via direct
19 * access to the shadow password file.
20 *
21 * If you decide to use PAM must be told how to handle run_init. A
22 * good rule-of-thumb might be to tell PAM to handle run_init in the
23 * same way it handles su, except that you should remove the pam_rootok.so
24 * entry so that even root must re-authenticate to run the init scripts
25 * in the proper context.
26 *
27 * If you choose not to use PAM, make sure you have a shadow passwd file
28 * in /etc/shadow. You can use a simlink if your shadow passwd file
29 * lives in another directory. Example:
30 * su
31 * cd /etc
32 * ln -s /etc/auth/shadow shadow
33 *
34 * If you decide not to use PAM, you will also have to make run_init
35 * setuid root, so that it can read the shadow passwd file.
36 *
37 *
38 *************************************************************************/
39
40 #include <stdio.h>
41 #include <stdlib.h> /* for malloc(), realloc(), free() */
42 #include <pwd.h> /* for getpwuid() */
43 #include <sys/types.h> /* to make getuid() and getpwuid() happy */
44 #include <sys/wait.h> /* for wait() */
45 #include <sys/stat.h> /* for struct stat and friends */
46 #include <getopt.h> /* for getopt_long() form of getopt() */
47 #include <selinux/selinux.h>
48 #include <selinux/get_default_type.h>
49 #include <selinux/context.h> /* for context-mangling functions */
50 #include <fcntl.h>
51 #include <ctype.h>
52 #include <limits.h>
53 #ifdef USE_AUDIT
54 #include <libaudit.h>
55 #endif
56 #ifdef USE_NLS
57 #include <libintl.h>
58 #include <locale.h>
59 #define _(msgid) gettext (msgid)
60 #else
61 #define _(msgid) (msgid)
62 #endif
63 #ifndef PACKAGE
64 #define PACKAGE "policycoreutils" /* the name of this package lang translation */
65 #endif
66 /* USAGE_STRING describes the command-line args of this program. */
67 #define USAGE_STRING _("USAGE: run_init <script> <args ...>\n\
68 where: <script> is the name of the init script to run,\n\
69 <args ...> are the arguments to that script.")
70
71 #define CONTEXT_FILE "initrc_context"
72 #ifdef USE_PAM
73
74 /************************************************************************
75 *
76 * All PAM code goes in this section.
77 *
78 ************************************************************************/
79
80 #include <unistd.h> /* for getuid(), exit(), getopt() */
81
82 #include <security/pam_appl.h> /* for PAM functions */
83 #include <security/pam_misc.h> /* for misc_conv PAM utility function */
84
85 #define SERVICE_NAME "run_init" /* the name of this program for PAM */
86 /* The file containing the context to run
87 * the scripts under. */
88
89 int authenticate_via_pam(const struct passwd *);
90
91 /* authenticate_via_pam()
92 *
93 * in: p_passwd_line - struct containing data from our user's line in
94 * the passwd file.
95 * out: nothing
96 * return: value condition
97 * ----- ---------
98 * 1 PAM thinks that the user authenticated themselves properly
99 * 0 otherwise
100 *
101 * This function uses PAM to authenticate the user running this
102 * program. This is the only function in this program that makes PAM
103 * calls.
104 *
105 */
106
authenticate_via_pam(const struct passwd * p_passwd_line)107 int authenticate_via_pam(const struct passwd *p_passwd_line)
108 {
109
110 int result = 0; /* our result, set to 0 (not authenticated) by default */
111 pam_handle_t *pam_handle; /* opaque handle used by all PAM functions */
112
113 /* This is a jump table of functions for PAM to use when it wants to *
114 * communicate with the user. We'll be using misc_conv(), which is *
115 * provided for us via pam_misc.h. */
116 struct pam_conv pam_conversation = {
117 misc_conv,
118 NULL
119 };
120
121 /* Make `p_pam_handle' a valid PAM handle so we can use it when *
122 * calling PAM functions. */
123 if (PAM_SUCCESS != pam_start(SERVICE_NAME,
124 p_passwd_line->pw_name,
125 &pam_conversation, &pam_handle)) {
126 fprintf(stderr, _("failed to initialize PAM\n"));
127 exit(-1);
128 }
129
130 /* Ask PAM to authenticate the user running this program */
131 if (PAM_SUCCESS == pam_authenticate(pam_handle, 0)) {
132 result = 1; /* user authenticated OK! */
133 }
134
135 /* If we were successful, call pam_acct_mgmt() to reset the
136 * pam_tally failcount.
137 */
138 if (result && (PAM_SUCCESS != pam_acct_mgmt(pam_handle, 0)) ) {
139 fprintf(stderr, _("failed to get account information\n"));
140 exit(-1);
141 }
142
143 /* We're done with PAM. Free `pam_handle'. */
144 pam_end(pam_handle, PAM_SUCCESS);
145
146 return (result);
147
148 } /* authenticate_via_pam() */
149
150 #else /* else !USE_PAM */
151
152 /************************************************************************
153 *
154 * All shadow passwd code goes in this section.
155 *
156 ************************************************************************/
157
158 #include <unistd.h> /* for getuid(), exit(), crypt() */
159 #include <shadow.h> /* for shadow passwd functions */
160 #include <string.h> /* for strlen(), memset() */
161
162 #define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */
163
164 int authenticate_via_shadow_passwd(const struct passwd *);
165
166 /* authenticate_via_shadow_passwd()
167 *
168 * in: p_passwd_line - struct containing data from our user's line in
169 * the passwd file.
170 * out: nothing
171 * return: value condition
172 * ----- ---------
173 * 1 user authenticated themselves properly according to the
174 * shadow passwd file.
175 * 0 otherwise
176 *
177 * This function uses the shadow passwd file to authenticate the user running
178 * this program.
179 *
180 */
181
authenticate_via_shadow_passwd(const struct passwd * p_passwd_line)182 int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line)
183 {
184
185 struct spwd *p_shadow_line; /* struct derived from shadow passwd file line */
186 char *unencrypted_password_s; /* unencrypted password input by user */
187 char *encrypted_password_s; /* user's password input after being crypt()ed */
188
189 /* Make `p_shadow_line' point to the data from the current user's *
190 * line in the shadow passwd file. */
191 setspent(); /* Begin access to the shadow passwd file. */
192 p_shadow_line = getspnam(p_passwd_line->pw_name);
193 endspent(); /* End access to the shadow passwd file. */
194 if (!(p_shadow_line)) {
195 fprintf(stderr,
196 _
197 ("Cannot find your entry in the shadow passwd file.\n"));
198 exit(-1);
199 }
200
201 /* Ask user to input unencrypted password */
202 if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
203 fprintf(stderr, _("getpass cannot open /dev/tty\n"));
204 exit(-1);
205 }
206
207 /* Use crypt() to encrypt user's input password. Clear the *
208 * unencrypted password as soon as we're done, so it is not *
209 * visible to memory snoopers. */
210 encrypted_password_s = crypt(unencrypted_password_s,
211 p_shadow_line->sp_pwdp);
212 memset(unencrypted_password_s, 0, strlen(unencrypted_password_s));
213
214 /* Return 1 (authenticated) iff the encrypted version of the user's *
215 * input password matches the encrypted password stored in the *
216 * shadow password file. */
217 return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp));
218
219 } /* authenticate_via_shadow_passwd() */
220
221 #endif /* if/else USE_PAM */
222
223 /*
224 * authenticate_user()
225 *
226 * Authenticate the user.
227 *
228 * in: nothing
229 * out: nothing
230 * return: 0 When success
231 * -1 When failure
232 */
authenticate_user(void)233 int authenticate_user(void)
234 {
235
236 #define INITLEN 255
237 struct passwd *p_passwd_line; /* struct derived from passwd file line */
238 uid_t uid;
239
240 /*
241 * Determine the Linux user identity to re-authenticate.
242 * If supported and set, use the login uid, as this should be more stable.
243 * Otherwise, use the real uid.
244 * The SELinux user identity is no longer used, as Linux users are now
245 * mapped to SELinux users via seusers and the SELinux user identity space
246 * is separate.
247 */
248 #ifdef USE_AUDIT
249 uid = audit_getloginuid();
250 if (uid == (uid_t) - 1)
251 uid = getuid();
252 #else
253 uid = getuid();
254 #endif
255
256 p_passwd_line = getpwuid(uid);
257 if (!p_passwd_line) {
258 fprintf(stderr, "cannot find your entry in the passwd file.\n");
259 return (-1);
260 }
261
262 printf("Authenticating %s.\n", p_passwd_line->pw_name);
263
264 /*
265 * Re-authenticate the user running this program.
266 * This is just to help confirm user intent (vs. invocation by
267 * malicious software), not to authorize the operation (which is covered
268 * by policy). Trusted path mechanism would be preferred.
269 */
270 #ifdef USE_PAM
271 if (!authenticate_via_pam(p_passwd_line)) {
272 #else /* !USE_PAM */
273 if (!authenticate_via_shadow_passwd(p_passwd_line)) {
274 #endif /* if/else USE_PAM */
275 fprintf(stderr, _("run_init: incorrect password for %s\n"),
276 p_passwd_line->pw_name);
277 return (-1);
278 }
279
280 /* If we reach here, then we have authenticated the user. */
281 #ifdef CANTSPELLGDB
282 printf("You are authenticated!\n");
283 #endif
284
285 return 0;
286
287 } /* authenticate_user() */
288
289 /*
290 * get_init_context()
291 *
292 * Get the CONTEXT associated with the context for the init scripts. *
293 *
294 * in: nothing
295 * out: The CONTEXT associated with the context.
296 * return: 0 on success, -1 on failure.
297 */
298 int get_init_context(security_context_t * context)
299 {
300
301 FILE *fp;
302 char buf[255], *bufp;
303 int buf_len;
304 char context_file[PATH_MAX];
305 snprintf(context_file, sizeof(context_file) - 1, "%s/%s",
306 selinux_contexts_path(), CONTEXT_FILE);
307 fp = fopen(context_file, "r");
308 if (!fp) {
309 fprintf(stderr, _("Could not open file %s\n"), context_file);
310 return -1;
311 }
312
313 while (1) { /* loop until we find a non-empty line */
314
315 if (!fgets(buf, sizeof buf, fp))
316 break;
317
318 buf_len = strlen(buf);
319 if (buf[buf_len - 1] == '\n')
320 buf[buf_len - 1] = 0;
321
322 bufp = buf;
323 while (*bufp && isspace(*bufp))
324 bufp++;
325
326 if (*bufp) {
327 *context = strdup(bufp);
328 if (!(*context))
329 goto out;
330 fclose(fp);
331 return 0;
332 }
333 }
334 out:
335 fclose(fp);
336 fprintf(stderr, _("No context in file %s\n"), context_file);
337 return -1;
338
339 } /* get_init_context() */
340
341 /*****************************************************************************
342 * main() *
343 *****************************************************************************/
344 int main(int argc, char *argv[])
345 {
346
347 extern char *optarg; /* used by getopt() for arg strings */
348 extern int opterr; /* controls getopt() error messages */
349 security_context_t new_context; /* context for the init script context */
350
351 #ifdef USE_NLS
352 setlocale(LC_ALL, "");
353 bindtextdomain(PACKAGE, LOCALEDIR);
354 textdomain(PACKAGE);
355 #endif
356
357 /* Verify that we are running on a flask-enabled kernel. */
358 if (!is_selinux_enabled()) {
359 fprintf(stderr,
360 _
361 ("Sorry, run_init may be used only on a SELinux kernel.\n"));
362 exit(-1);
363 }
364
365 /*
366 * Step 1: Handle command-line arguments. The first argument is the
367 * name of the script to run. All other arguments are for the script
368 * itself, and will be passed directly to the script.
369 */
370
371 if (argc < 2) {
372 fprintf(stderr, "%s\n", USAGE_STRING);
373 exit(-1);
374 }
375
376 /*
377 * Step 2: Authenticate the user.
378 */
379 if (authenticate_user() != 0) {
380 fprintf(stderr, _("authentication failed.\n"));
381 exit(-1);
382 }
383
384 /*
385 * Step 3: Get the context for the script to be run in.
386 */
387 if (get_init_context(&new_context) == 0) {
388 #ifdef CANTSPELLGDB
389 printf("context is %s\n", new_context);
390 #endif
391 } else {
392 exit(-1);
393 }
394
395 /*
396 * Step 4: Run the command in the correct context.
397 */
398
399 if (chdir("/")) {
400 perror("chdir");
401 exit(-1);
402 }
403
404 if (setexeccon(new_context) < 0) {
405 fprintf(stderr, _("Could not set exec context to %s.\n"),
406 new_context);
407 exit(-1);
408 }
409 if (access("/usr/sbin/open_init_pty", X_OK) != 0) {
410 if (execvp(argv[1], argv + 1)) {
411 perror("execvp");
412 exit(-1);
413 }
414 return 0;
415 }
416 /*
417 * Do not execvp the command directly from run_init; since it would run
418 * under with a pty under sysadm_devpts_t. Instead, we call open_init_tty,
419 * which transitions us into initrc_t, which then spawns a new
420 * process, that gets a pty with context initrc_devpts_t. Just
421 * execvp or using a exec(1) recycles pty's, and does not open a new
422 * one.
423 */
424 if (execvp("/usr/sbin/open_init_pty", argv)) {
425 perror("execvp");
426 exit(-1);
427 }
428 return 0;
429
430 } /* main() */
431