• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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