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