File: run_init.c

package info (click to toggle)
android-platform-external-libselinux 10.0.0%2Br36-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 34,176 kB
  • sloc: ansic: 147,112; python: 25,790; makefile: 1,930; yacc: 1,389; sh: 1,206; lex: 452; xml: 180
file content (430 lines) | stat: -rw-r--r-- 12,413 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
/************************************************************************
 *
 * run_init
 *
 * SYNOPSIS:
 *
 * This program allows a user to run an /etc/init.d script in the proper context.
 *
 * USAGE:
 *
 * run_init <script> <args>
 *
 * BUILD OPTIONS:
 *
 * option USE_PAM:
 *
 * Set the USE_PAM constant if you want to authenticate users via PAM.
 * If USE_PAM is not set, users will be authenticated via direct
 * access to the shadow password file.
 *
 * If you decide to use PAM must be told how to handle run_init.  A
 * good rule-of-thumb might be to tell PAM to handle run_init in the
 * same way it handles su, except that you should remove the pam_rootok.so
 * entry so that even root must re-authenticate to run the init scripts
 * in the proper context.
 *
 * If you choose not to use PAM, make sure you have a shadow passwd file
 * in /etc/shadow.  You can use a simlink if your shadow passwd file
 * lives in another directory.  Example:
 *   su
 *   cd /etc
 *   ln -s /etc/auth/shadow shadow
 *
 * If you decide not to use PAM, you will also have to make run_init
 * setuid root, so that it can read the shadow passwd file.
 * 
 *
 *************************************************************************/

#include <stdio.h>
#include <stdlib.h>		/* for malloc(), realloc(), free() */
#include <pwd.h>		/* for getpwuid() */
#include <sys/types.h>		/* to make getuid() and getpwuid() happy */
#include <sys/wait.h>		/* for wait() */
#include <sys/stat.h>		/* for struct stat and friends */
#include <getopt.h>		/* for getopt_long() form of getopt() */
#include <selinux/selinux.h>
#include <selinux/get_default_type.h>
#include <selinux/context.h>	/* for context-mangling functions */
#include <fcntl.h>
#include <ctype.h>
#include <limits.h>
#ifdef USE_AUDIT
#include <libaudit.h>
#endif
#ifdef USE_NLS
#include <libintl.h>
#include <locale.h>
#define _(msgid) gettext (msgid)
#else
#define _(msgid) (msgid)
#endif
#ifndef PACKAGE
#define PACKAGE "policycoreutils"	/* the name of this package lang translation */
#endif
/* USAGE_STRING describes the command-line args of this program. */
#define USAGE_STRING _("USAGE: run_init <script> <args ...>\n\
  where: <script> is the name of the init script to run,\n\
         <args ...> are the arguments to that script.")

#define CONTEXT_FILE "initrc_context"
#ifdef USE_PAM

/************************************************************************
 *
 * All PAM code goes in this section.
 *
 ************************************************************************/

#include <unistd.h>		/* for getuid(), exit(), getopt() */

#include <security/pam_appl.h>	/* for PAM functions */
#include <security/pam_misc.h>	/* for misc_conv PAM utility function */

#define SERVICE_NAME "run_init"	/* the name of this program for PAM */
				  /* The file containing the context to run 
				   * the scripts under.                     */

int authenticate_via_pam(const struct passwd *);

/* authenticate_via_pam()
 *
 * in:     p_passwd_line - struct containing data from our user's line in 
 *                         the passwd file.
 * out:    nothing
 * return: value   condition
 *         -----   ---------
 *           1     PAM thinks that the user authenticated themselves properly
 *           0     otherwise
 *
 * This function uses PAM to authenticate the user running this
 * program.  This is the only function in this program that makes PAM
 * calls.
 *
 */

int authenticate_via_pam(const struct passwd *p_passwd_line)
{

	int result = 0;		/* our result, set to 0 (not authenticated) by default */
	pam_handle_t *pam_handle;	/* opaque handle used by all PAM functions */

	/* This is a jump table of functions for PAM to use when it wants to *
	 * communicate with the user.  We'll be using misc_conv(), which is  *
	 * provided for us via pam_misc.h.                                   */
	struct pam_conv pam_conversation = {
		misc_conv,
		NULL
	};

	/* Make `p_pam_handle' a valid PAM handle so we can use it when *
	 * calling PAM functions.                                       */
	if (PAM_SUCCESS != pam_start(SERVICE_NAME,
				     p_passwd_line->pw_name,
				     &pam_conversation, &pam_handle)) {
		fprintf(stderr, _("failed to initialize PAM\n"));
		exit(-1);
	}

	/* Ask PAM to authenticate the user running this program */
	if (PAM_SUCCESS == pam_authenticate(pam_handle, 0)) {
		result = 1;	/* user authenticated OK! */
	}

	/* If we were successful, call pam_acct_mgmt() to reset the
         * pam_tally failcount.
         */
	if (result && (PAM_SUCCESS != pam_acct_mgmt(pam_handle, 0)) ) {
		fprintf(stderr, _("failed to get account information\n"));
		exit(-1);
	}	

	/* We're done with PAM.  Free `pam_handle'. */
	pam_end(pam_handle, PAM_SUCCESS);

	return (result);

}				/* authenticate_via_pam() */

#else				/* else !USE_PAM */

/************************************************************************
 *
 * All shadow passwd code goes in this section.
 *
 ************************************************************************/

#include <unistd.h>		/* for getuid(), exit(), crypt() */
#include <shadow.h>		/* for shadow passwd functions */
#include <string.h>		/* for strlen(), memset() */

#define PASSWORD_PROMPT _("Password:")	/* prompt for getpass() */

int authenticate_via_shadow_passwd(const struct passwd *);

/* authenticate_via_shadow_passwd()
 *
 * in:     p_passwd_line - struct containing data from our user's line in 
 *                         the passwd file.
 * out:    nothing
 * return: value   condition
 *         -----   ---------
 *           1     user authenticated themselves properly according to the
 *                 shadow passwd file.
 *           0     otherwise
 *
 * This function uses the shadow passwd file to authenticate the user running
 * this program.
 *
 */

int authenticate_via_shadow_passwd(const struct passwd *p_passwd_line)
{

	struct spwd *p_shadow_line;	/* struct derived from shadow passwd file line */
	char *unencrypted_password_s;	/* unencrypted password input by user */
	char *encrypted_password_s;	/* user's password input after being crypt()ed */

	/* Make `p_shadow_line' point to the data from the current user's *
	 * line in the shadow passwd file.                                */
	setspent();		/* Begin access to the shadow passwd file. */
	p_shadow_line = getspnam(p_passwd_line->pw_name);
	endspent();		/* End access to the shadow passwd file. */
	if (!(p_shadow_line)) {
		fprintf(stderr,
			_
			("Cannot find your entry in the shadow passwd file.\n"));
		exit(-1);
	}

	/* Ask user to input unencrypted password */
	if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
		fprintf(stderr, _("getpass cannot open /dev/tty\n"));
		exit(-1);
	}

	/* Use crypt() to encrypt user's input password.  Clear the *
	 * unencrypted password as soon as we're done, so it is not * 
	 * visible to memory snoopers.                              */
	encrypted_password_s = crypt(unencrypted_password_s,
				     p_shadow_line->sp_pwdp);
	memset(unencrypted_password_s, 0, strlen(unencrypted_password_s));

	/* Return 1 (authenticated) iff the encrypted version of the user's *
	 * input password matches the encrypted password stored in the      *
	 * shadow password file.                                            */
	return (!strcmp(encrypted_password_s, p_shadow_line->sp_pwdp));

}				/* authenticate_via_shadow_passwd() */

#endif				/* if/else USE_PAM */

/*
 * authenticate_user()
 *
 * Authenticate the user.
 *
 * in:		nothing
 * out:		nothing
 * return:	0 When success
 *		-1 When failure
 */
int authenticate_user(void)
{

#define INITLEN 255
	struct passwd *p_passwd_line;	/* struct derived from passwd file line */
	uid_t uid;

	/*
	 * Determine the Linux user identity to re-authenticate.
	 * If supported and set, use the login uid, as this should be more stable.
	 * Otherwise, use the real uid.
	 * The SELinux user identity is no longer used, as Linux users are now
	 * mapped to SELinux users via seusers and the SELinux user identity space
	 * is separate.
	 */
#ifdef USE_AUDIT
	uid = audit_getloginuid();
	if (uid == (uid_t) - 1)
		uid = getuid();
#else
	uid = getuid();
#endif

	p_passwd_line = getpwuid(uid);
	if (!p_passwd_line) {
		fprintf(stderr, "cannot find your entry in the passwd file.\n");
		return (-1);
	}

	printf("Authenticating %s.\n", p_passwd_line->pw_name);

	/* 
	 * Re-authenticate the user running this program.
	 * This is just to help confirm user intent (vs. invocation by
	 * malicious software), not to authorize the operation (which is covered
	 * by policy).  Trusted path mechanism would be preferred.
	 */
#ifdef USE_PAM
	if (!authenticate_via_pam(p_passwd_line)) {
#else				/* !USE_PAM */
	if (!authenticate_via_shadow_passwd(p_passwd_line)) {
#endif				/* if/else USE_PAM */
		fprintf(stderr, _("run_init: incorrect password for %s\n"),
			p_passwd_line->pw_name);
		return (-1);
	}

	/* If we reach here, then we have authenticated the user. */
#ifdef CANTSPELLGDB
	printf("You are authenticated!\n");
#endif

	return 0;

}				/* authenticate_user() */

/*
 * get_init_context()
 *
 * Get the CONTEXT associated with the context for the init scripts.             *
 *
 * in:		nothing
 * out:		The CONTEXT associated with the context.
 * return:	0 on success, -1 on failure.
 */
int get_init_context(security_context_t * context)
{

	FILE *fp;
	char buf[255], *bufp;
	int buf_len;
	char context_file[PATH_MAX];
	snprintf(context_file, sizeof(context_file) - 1, "%s/%s",
		 selinux_contexts_path(), CONTEXT_FILE);
	fp = fopen(context_file, "r");
	if (!fp) {
		fprintf(stderr, _("Could not open file %s\n"), context_file);
		return -1;
	}

	while (1) {		/* loop until we find a non-empty line */

		if (!fgets(buf, sizeof buf, fp))
			break;

		buf_len = strlen(buf);
		if (buf[buf_len - 1] == '\n')
			buf[buf_len - 1] = 0;

		bufp = buf;
		while (*bufp && isspace(*bufp))
			bufp++;

		if (*bufp) {
			*context = strdup(bufp);
			if (!(*context))
				goto out;
			fclose(fp);
			return 0;
		}
	}
      out:
	fclose(fp);
	fprintf(stderr, _("No context in file %s\n"), context_file);
	return -1;

}				/* get_init_context() */

/*****************************************************************************
 * main()                                                                    *
 *****************************************************************************/
int main(int argc, char *argv[])
{

	extern char *optarg;	/* used by getopt() for arg strings */
	extern int opterr;	/* controls getopt() error messages */
	security_context_t new_context;	/* context for the init script context  */

#ifdef USE_NLS
	setlocale(LC_ALL, "");
	bindtextdomain(PACKAGE, LOCALEDIR);
	textdomain(PACKAGE);
#endif

	/* Verify that we are running on a flask-enabled kernel. */
	if (!is_selinux_enabled()) {
		fprintf(stderr,
			_
			("Sorry, run_init may be used only on a SELinux kernel.\n"));
		exit(-1);
	}

	/*
	 * Step 1:  Handle command-line arguments. The first argument is the 
	 * name of the script to run. All other arguments are for the script
	 * itself, and will be passed directly to the script.
	 */

	if (argc < 2) {
		fprintf(stderr, "%s\n", USAGE_STRING);
		exit(-1);
	}

	/*
	 * Step 2:  Authenticate the user.
	 */
	if (authenticate_user() != 0) {
		fprintf(stderr, _("authentication failed.\n"));
		exit(-1);
	}

	/*
	 * Step 3: Get the context for the script to be run in.
	 */
	if (get_init_context(&new_context) == 0) {
#ifdef CANTSPELLGDB
		printf("context is %s\n", new_context);
#endif
	} else {
		exit(-1);
	}

	/*
	 * Step 4: Run the command in the correct context.
	 */

	if (chdir("/")) {
		perror("chdir");
		exit(-1);
	}

	if (setexeccon(new_context) < 0) {
		fprintf(stderr, _("Could not set exec context to %s.\n"),
			new_context);
		exit(-1);
	}
	if (access("/usr/sbin/open_init_pty", X_OK) != 0) {
		if (execvp(argv[1], argv + 1)) {
			perror("execvp");
			exit(-1);
		}
		return 0;
	}
	/*
	 * Do not execvp the command directly from run_init; since it would run
	 * under with a pty under sysadm_devpts_t. Instead, we call open_init_tty,
	 * which transitions us into initrc_t, which then spawns a new
	 * process, that gets a pty with context initrc_devpts_t. Just
	 * execvp or using a exec(1) recycles pty's, and does not open a new
	 * one. 
	 */
	if (execvp("/usr/sbin/open_init_pty", argv)) {
		perror("execvp");
		exit(-1);
	}
	return 0;

}				/* main() */