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 /* authenticate_via_pam()
90 *
91 * in: p_passwd_line - struct containing data from our user's line in
92 * the passwd file.
93 * out: nothing
94 * return: value condition
95 * ----- ---------
96 * 1 PAM thinks that the user authenticated themselves properly
97 * 0 otherwise
98 *
99 * This function uses PAM to authenticate the user running this
100 * program. This is the only function in this program that makes PAM
101 * calls.
102 *
103 */
104
authenticate_via_pam(const struct passwd * p_passwd_line)105 static int authenticate_via_pam(const struct passwd *p_passwd_line)
106 {
107
108 int result = 0; /* our result, set to 0 (not authenticated) by default */
109 pam_handle_t *pam_handle; /* opaque handle used by all PAM functions */
110
111 /* This is a jump table of functions for PAM to use when it wants to *
112 * communicate with the user. We'll be using misc_conv(), which is *
113 * provided for us via pam_misc.h. */
114 struct pam_conv pam_conversation = {
115 misc_conv,
116 NULL
117 };
118
119 /* Make `p_pam_handle' a valid PAM handle so we can use it when *
120 * calling PAM functions. */
121 if (PAM_SUCCESS != pam_start(SERVICE_NAME,
122 p_passwd_line->pw_name,
123 &pam_conversation, &pam_handle)) {
124 fprintf(stderr, _("failed to initialize PAM\n"));
125 exit(-1);
126 }
127
128 /* Ask PAM to authenticate the user running this program */
129 if (PAM_SUCCESS == pam_authenticate(pam_handle, 0)) {
130 result = 1; /* user authenticated OK! */
131 }
132
133 /* If we were successful, call pam_acct_mgmt() to reset the
134 * pam_tally failcount.
135 */
136 if (result && (PAM_SUCCESS != pam_acct_mgmt(pam_handle, 0)) ) {
137 fprintf(stderr, _("failed to get account information\n"));
138 exit(-1);
139 }
140
141 /* We're done with PAM. Free `pam_handle'. */
142 pam_end(pam_handle, PAM_SUCCESS);
143
144 return (result);
145
146 } /* authenticate_via_pam() */
147
148 #else /* else !USE_PAM */
149
150 /************************************************************************
151 *
152 * All shadow passwd code goes in this section.
153 *
154 ************************************************************************/
155
156 #include <unistd.h> /* for getuid(), exit(), crypt() */
157 #include <shadow.h> /* for shadow passwd functions */
158 #include <string.h> /* for strlen(), memset() */
159
160 /*
161 * crypt() may not be defined in unistd.h; see:
162 * http://man7.org/linux/man-pages/man3/crypt.3.html#NOTES
163 */
164 #if !defined(_XOPEN_CRYPT) || _XOPEN_CRYPT == -1
165 #include <crypt.h>
166 #endif
167
168 #define PASSWORD_PROMPT _("Password:") /* prompt for getpass() */
169
170 /* authenticate_via_shadow_passwd()
171 *
172 * in: p_passwd_line - struct containing data from our user's line in
173 * the passwd file.
174 * out: nothing
175 * return: value condition
176 * ----- ---------
177 * 1 user authenticated themselves properly according to the
178 * shadow passwd file.
179 * 0 otherwise
180 *
181 * This function uses the shadow passwd file to authenticate the user running
182 * this program.
183 *
184 */
185
authenticate_via_shadow_passwd(const struct passwd * p_passwd_line)186 static int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line)
187 {
188
189 struct spwd *p_shadow_line; /* struct derived from shadow passwd file line */
190 char *unencrypted_password_s; /* unencrypted password input by user */
191 char *encrypted_password_s; /* user's password input after being crypt()ed */
192
193 /* Make `p_shadow_line' point to the data from the current user's *
194 * line in the shadow passwd file. */
195 setspent(); /* Begin access to the shadow passwd file. */
196 p_shadow_line = getspnam(p_passwd_line->pw_name);
197 endspent(); /* End access to the shadow passwd file. */
198 if (!(p_shadow_line)) {
199 fprintf(stderr,
200 _
201 ("Cannot find your entry in the shadow passwd file.\n"));
202 exit(-1);
203 }
204
205 /* Ask user to input unencrypted password */
206 if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
207 fprintf(stderr, _("getpass cannot open /dev/tty\n"));
208 exit(-1);
209 }
210
211 /* Use crypt() to encrypt user's input password. Clear the *
212 * unencrypted password as soon as we're done, so it is not *
213 * visible to memory snoopers. */
214 encrypted_password_s = crypt(unencrypted_password_s,
215 p_shadow_line->sp_pwdp);
216 memset(unencrypted_password_s, 0, strlen(unencrypted_password_s));
217
218 /* Return 1 (authenticated) iff the encrypted version of the user's *
219 * input password matches the encrypted password stored in the *
220 * shadow password file. */
221 return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp));
222
223 } /* authenticate_via_shadow_passwd() */
224
225 #endif /* if/else USE_PAM */
226
227 /*
228 * authenticate_user()
229 *
230 * Authenticate the user.
231 *
232 * in: nothing
233 * out: nothing
234 * return: 0 When success
235 * -1 When failure
236 */
authenticate_user(void)237 static int authenticate_user(void)
238 {
239
240 #define INITLEN 255
241 struct passwd *p_passwd_line; /* struct derived from passwd file line */
242 uid_t uid;
243
244 /*
245 * Determine the Linux user identity to re-authenticate.
246 * If supported and set, use the login uid, as this should be more stable.
247 * Otherwise, use the real uid.
248 * The SELinux user identity is no longer used, as Linux users are now
249 * mapped to SELinux users via seusers and the SELinux user identity space
250 * is separate.
251 */
252 #ifdef USE_AUDIT
253 uid = audit_getloginuid();
254 if (uid == (uid_t) - 1)
255 uid = getuid();
256 #else
257 uid = getuid();
258 #endif
259
260 p_passwd_line = getpwuid(uid);
261 if (!p_passwd_line) {
262 fprintf(stderr, "cannot find your entry in the passwd file.\n");
263 return (-1);
264 }
265
266 printf("Authenticating %s.\n", p_passwd_line->pw_name);
267
268 /*
269 * Re-authenticate the user running this program.
270 * This is just to help confirm user intent (vs. invocation by
271 * malicious software), not to authorize the operation (which is covered
272 * by policy). Trusted path mechanism would be preferred.
273 */
274 #ifdef USE_PAM
275 if (!authenticate_via_pam(p_passwd_line)) {
276 #else /* !USE_PAM */
277 if (!authenticate_via_shadow_passwd(p_passwd_line)) {
278 #endif /* if/else USE_PAM */
279 fprintf(stderr, _("run_init: incorrect password for %s\n"),
280 p_passwd_line->pw_name);
281 return (-1);
282 }
283
284 /* If we reach here, then we have authenticated the user. */
285 #ifdef CANTSPELLGDB
286 printf("You are authenticated!\n");
287 #endif
288
289 return 0;
290
291 } /* authenticate_user() */
292
293 /*
294 * get_init_context()
295 *
296 * Get the CONTEXT associated with the context for the init scripts. *
297 *
298 * in: nothing
299 * out: The CONTEXT associated with the context.
300 * return: 0 on success, -1 on failure.
301 */
302 static int get_init_context(char **context)
303 {
304
305 FILE *fp;
306 char buf[255], *bufp;
307 int buf_len;
308 char context_file[PATH_MAX];
309 snprintf(context_file, sizeof(context_file) - 1, "%s/%s",
310 selinux_contexts_path(), CONTEXT_FILE);
311 fp = fopen(context_file, "r");
312 if (!fp) {
313 fprintf(stderr, _("Could not open file %s\n"), context_file);
314 return -1;
315 }
316
317 while (1) { /* loop until we find a non-empty line */
318
319 if (!fgets(buf, sizeof buf, fp))
320 break;
321
322 buf_len = strlen(buf);
323 if (buf[buf_len - 1] == '\n')
324 buf[buf_len - 1] = 0;
325
326 bufp = buf;
327 while (*bufp && isspace(*bufp))
328 bufp++;
329
330 if (*bufp) {
331 *context = strdup(bufp);
332 if (!(*context))
333 goto out;
334 fclose(fp);
335 return 0;
336 }
337 }
338 out:
339 fclose(fp);
340 fprintf(stderr, _("No context in file %s\n"), context_file);
341 return -1;
342
343 } /* get_init_context() */
344
345 /*****************************************************************************
346 * main() *
347 *****************************************************************************/
348 int main(int argc, char *argv[])
349 {
350
351 extern char *optarg; /* used by getopt() for arg strings */
352 extern int opterr; /* controls getopt() error messages */
353 char *new_context; /* context for the init script context */
354
355 #ifdef USE_NLS
356 setlocale(LC_ALL, "");
357 bindtextdomain(PACKAGE, LOCALEDIR);
358 textdomain(PACKAGE);
359 #endif
360
361 /* Verify that we are running on a flask-enabled kernel. */
362 if (!is_selinux_enabled()) {
363 fprintf(stderr,
364 _
365 ("Sorry, run_init may be used only on a SELinux kernel.\n"));
366 exit(-1);
367 }
368
369 /*
370 * Step 1: Handle command-line arguments. The first argument is the
371 * name of the script to run. All other arguments are for the script
372 * itself, and will be passed directly to the script.
373 */
374
375 if (argc < 2) {
376 fprintf(stderr, "%s\n", USAGE_STRING);
377 exit(-1);
378 }
379
380 /*
381 * Step 2: Authenticate the user.
382 */
383 if (authenticate_user() != 0) {
384 fprintf(stderr, _("authentication failed.\n"));
385 exit(-1);
386 }
387
388 /*
389 * Step 3: Get the context for the script to be run in.
390 */
391 if (get_init_context(&new_context) == 0) {
392 #ifdef CANTSPELLGDB
393 printf("context is %s\n", new_context);
394 #endif
395 } else {
396 exit(-1);
397 }
398
399 /*
400 * Step 4: Run the command in the correct context.
401 */
402
403 if (chdir("/")) {
404 perror("chdir");
405 free(new_context);
406 exit(-1);
407 }
408
409 if (setexeccon(new_context) < 0) {
410 fprintf(stderr, _("Could not set exec context to %s.\n"),
411 new_context);
412 free(new_context);
413 exit(-1);
414 }
415
416 free(new_context);
417
418 if (access("/usr/sbin/open_init_pty", X_OK) != 0) {
419 if (execvp(argv[1], argv + 1)) {
420 perror("execvp");
421 exit(-1);
422 }
423 return 0;
424 }
425 /*
426 * Do not execvp the command directly from run_init; since it would run
427 * under with a pty under sysadm_devpts_t. Instead, we call open_init_tty,
428 * which transitions us into initrc_t, which then spawns a new
429 * process, that gets a pty with context initrc_devpts_t. Just
430 * execvp or using a exec(1) recycles pty's, and does not open a new
431 * one.
432 */
433 if (execvp("/usr/sbin/open_init_pty", argv)) {
434 perror("execvp");
435 exit(-1);
436 }
437 return 0;
438
439 } /* main() */
440