Home | History | Annotate | Download | only in run_init
      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 
    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 
    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  */
    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