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
89int 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
107int 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
164int 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
182int 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 */
233int 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 */
298int 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 *****************************************************************************/
344int 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