xref: /aosp_15_r20/external/selinux/policycoreutils/newrole/newrole.c (revision 2d543d20722ada2425b5bdab9d0d1d29470e7bba)
1 /************************************************************************
2  *
3  * newrole
4  *
5  * SYNOPSIS:
6  *
7  * This program allows a user to change their SELinux RBAC role and/or
8  * SELinux TE type (domain) in a manner similar to the way the traditional
9  * UNIX su program allows a user to change their identity.
10  *
11  * USAGE:
12  *
13  * newrole [ -r role ] [ -t type ] [ -l level ] [ -V ] [ -- args ]
14  *
15  * BUILD OPTIONS:
16  *
17  * option USE_PAM:
18  *
19  * Set the USE_PAM constant if you want to authenticate users via PAM.
20  * If USE_PAM is not set, users will be authenticated via direct
21  * access to the shadow password file.
22  *
23  * If you decide to use PAM must be told how to handle newrole.  A
24  * good rule-of-thumb might be to tell PAM to handle newrole in the
25  * same way it handles su, except that you should remove the pam_rootok.so
26  * entry so that even root must re-authenticate to change roles.
27  *
28  * If you choose not to use PAM, make sure you have a shadow passwd file
29  * in /etc/shadow.  You can use a symlink if your shadow passwd file
30  * lives in another directory.  Example:
31  *   su
32  *   cd /etc
33  *   ln -s /etc/auth/shadow shadow
34  *
35  * If you decide not to use PAM, you will also have to make newrole
36  * setuid root, so that it can read the shadow passwd file.
37  *
38  *
39  * Authors:
40  *      Anthony Colatrella
41  *	Tim Fraser
42  *	Steve Grubb <[email protected]>
43  *	Darrel Goeddel <[email protected]>
44  *	Michael Thompson <[email protected]>
45  *	Dan Walsh <[email protected]>
46  *
47  *************************************************************************/
48 
49 #define _GNU_SOURCE
50 
51 #if defined(AUDIT_LOG_PRIV) && !defined(USE_AUDIT)
52 #error AUDIT_LOG_PRIV needs the USE_AUDIT option
53 #endif
54 #if defined(NAMESPACE_PRIV) && !defined(USE_PAM)
55 #error NAMESPACE_PRIV needs the USE_PAM option
56 #endif
57 
58 #include <stdio.h>
59 #include <stdlib.h>		/* for malloc(), realloc(), free() */
60 #include <pwd.h>		/* for getpwuid() */
61 #include <ctype.h>
62 #include <sys/types.h>		/* to make getuid() and getpwuid() happy */
63 #include <sys/wait.h>		/* for wait() */
64 #include <getopt.h>		/* for getopt_long() form of getopt() */
65 #include <fcntl.h>
66 #include <string.h>
67 #include <errno.h>
68 #include <selinux/selinux.h>	/* for is_selinux_enabled() */
69 #include <selinux/context.h>	/* for context-mangling functions */
70 #include <selinux/get_default_type.h>
71 #include <selinux/get_context_list.h>	/* for SELINUX_DEFAULTUSER */
72 #include <signal.h>
73 #include <unistd.h>		/* for getuid(), exit(), getopt() */
74 #ifdef USE_AUDIT
75 #include <libaudit.h>
76 #endif
77 #if defined(AUDIT_LOG_PRIV) || defined(NAMESPACE_PRIV)
78 #include <sys/prctl.h>
79 #include <cap-ng.h>
80 #endif
81 #ifdef USE_NLS
82 #include <locale.h>		/* for setlocale() */
83 #include <libintl.h>		/* for gettext() */
84 #define _(msgid) gettext (msgid)
85 #else
86 #define _(msgid) (msgid)
87 #endif
88 #ifndef PACKAGE
89 #define PACKAGE "policycoreutils"	/* the name of this package lang translation */
90 #endif
91 
92 #define TRUE 1
93 #define FALSE 0
94 
95 /* USAGE_STRING describes the command-line args of this program. */
96 #define USAGE_STRING "USAGE: newrole [ -r role ] [ -t type ] [ -l level ] [ -p ] [ -V ] [ -- args ]"
97 
98 #ifdef USE_PAM
99 #define PAM_SERVICE_CONFIG "/etc/selinux/newrole_pam.conf"
100 #endif
101 
102 #define DEFAULT_PATH "/usr/bin:/bin"
103 
104 extern char **environ;
105 
106 /**
107  * Construct from the current range and specified desired level a resulting
108  * range. If the specified level is a range, return that. If it is not, then
109  * construct a range with level as the sensitivity and clearance of the current
110  * context.
111  *
112  * newlevel - the level specified on the command line
113  * range    - the range in the current context
114  *
115  * Returns malloc'd memory
116  */
build_new_range(const char * newlevel,const char * range)117 static char *build_new_range(const char *newlevel, const char *range)
118 {
119 	char *newrangep = NULL;
120 	const char *tmpptr;
121 	size_t len;
122 
123 	/* a missing or empty string */
124 	if (!range || !strlen(range) || !newlevel || !strlen(newlevel))
125 		return NULL;
126 
127 	/* if the newlevel is actually a range - just use that */
128 	if (strchr(newlevel, '-')) {
129 		newrangep = strdup(newlevel);
130 		return newrangep;
131 	}
132 
133 	/* look for MLS range in current context */
134 	tmpptr = strchr(range, '-');
135 	if (tmpptr) {
136 		/* we are inserting into a ranged MLS context */
137 		len = strlen(newlevel) + 1 + strlen(tmpptr + 1) + 1;
138 		newrangep = (char *)malloc(len);
139 		if (!newrangep)
140 			return NULL;
141 		snprintf(newrangep, len, "%s-%s", newlevel, tmpptr + 1);
142 	} else {
143 		/* we are inserting into a currently non-ranged MLS context */
144 		if (!strcmp(newlevel, range)) {
145 			newrangep = strdup(range);
146 		} else {
147 			len = strlen(newlevel) + 1 + strlen(range) + 1;
148 			newrangep = (char *)malloc(len);
149 			if (!newrangep)
150 				return NULL;
151 			snprintf(newrangep, len, "%s-%s", newlevel, range);
152 		}
153 	}
154 
155 	return newrangep;
156 }
157 
158 #ifdef USE_PAM
159 
160 /************************************************************************
161  *
162  * All PAM code goes in this section.
163  *
164  ************************************************************************/
165 #include <security/pam_appl.h>	/* for PAM functions */
166 #include <security/pam_misc.h>	/* for misc_conv PAM utility function */
167 
168 static const char *service_name = "newrole";
169 
170 /* authenticate_via_pam()
171  *
172  * in:     pw - struct containing data from our user's line in
173  *                         the passwd file.
174  * out:    nothing
175  * return: value   condition
176  *         -----   ---------
177  *           1     PAM thinks that the user authenticated themselves properly
178  *           0     otherwise
179  *
180  * This function uses PAM to authenticate the user running this
181  * program.  This is the only function in this program that makes PAM
182  * calls.
183  */
authenticate_via_pam(const char * ttyn,pam_handle_t * pam_handle)184 static int authenticate_via_pam(const char *ttyn, pam_handle_t * pam_handle)
185 {
186 
187 	int result = 0;		/* set to 0 (not authenticated) by default */
188 	int pam_rc;		/* pam return code */
189 	const char *tty_name;
190 
191 	if (ttyn) {
192 		if (strncmp(ttyn, "/dev/", 5) == 0)
193 			tty_name = ttyn + 5;
194 		else
195 			tty_name = ttyn;
196 
197 		pam_rc = pam_set_item(pam_handle, PAM_TTY, tty_name);
198 		if (pam_rc != PAM_SUCCESS) {
199 			fprintf(stderr, _("failed to set PAM_TTY\n"));
200 			goto out;
201 		}
202 	}
203 
204 	/* Ask PAM to authenticate the user running this program */
205 	pam_rc = pam_authenticate(pam_handle, 0);
206 	if (pam_rc != PAM_SUCCESS) {
207 		goto out;
208 	}
209 
210 	/* Ask PAM to verify acct_mgmt */
211 	pam_rc = pam_acct_mgmt(pam_handle, 0);
212 	if (pam_rc == PAM_SUCCESS) {
213 		result = 1;	/* user authenticated OK! */
214 	}
215 
216       out:
217 	return result;
218 }				/* authenticate_via_pam() */
219 
220 #include "hashtab.h"
221 
free_hashtab_entry(hashtab_key_t key,hashtab_datum_t d,void * args)222 static int free_hashtab_entry(hashtab_key_t key, hashtab_datum_t d,
223 			      void *args __attribute__ ((unused)))
224 {
225 	free(key);
226 	free(d);
227 	return 0;
228 }
229 
reqsymhash(hashtab_t h,const_hashtab_key_t key)230 static unsigned int reqsymhash(hashtab_t h, const_hashtab_key_t key)
231 {
232 	unsigned int hash = 5381;
233 	unsigned char c;
234 
235 	while ((c = *(unsigned const char *)key++))
236 		hash = ((hash << 5) + hash) ^ c;
237 
238 	return hash & (h->size - 1);
239 }
240 
reqsymcmp(hashtab_t h,const_hashtab_key_t key1,const_hashtab_key_t key2)241 static int reqsymcmp(hashtab_t h
242 		     __attribute__ ((unused)), const_hashtab_key_t key1,
243 		     const_hashtab_key_t key2)
244 {
245 	return strcmp(key1, key2);
246 }
247 
248 static hashtab_t app_service_names = NULL;
249 #define PAM_SERVICE_SLOTS 64
250 
process_pam_config(FILE * cfg)251 static int process_pam_config(FILE * cfg)
252 {
253 	const char *config_file_path = PAM_SERVICE_CONFIG;
254 	char *line_buf = NULL;
255 	unsigned long lineno = 0;
256 	size_t len = 0;
257 	char *app = NULL;
258 	char *service = NULL;
259 	int ret;
260 
261 	while (getline(&line_buf, &len, cfg) > 0) {
262 		char *buffer = line_buf;
263 		lineno++;
264 		while (isspace(*buffer))
265 			buffer++;
266 		if (buffer[0] == '#')
267 			continue;
268 		if (buffer[0] == '\n' || buffer[0] == '\0')
269 			continue;
270 
271 		app = service = NULL;
272 		ret = sscanf(buffer, "%ms %ms\n", &app, &service);
273 		if (ret < 2 || !app || !service)
274 			goto err;
275 
276 		ret = hashtab_insert(app_service_names, app, service);
277 		if (ret == HASHTAB_OVERFLOW) {
278 			fprintf(stderr,
279 				_
280 				("newrole: service name configuration hashtable overflow\n"));
281 			goto err;
282 		}
283 	}
284 
285 	free(line_buf);
286 	return 0;
287       err:
288 	free(app);
289 	free(service);
290 	fprintf(stderr, _("newrole:  %s:  error on line %lu.\n"),
291 		config_file_path, lineno);
292 	free(line_buf);
293 	return -1;
294 }
295 
296 /*
297  *  Read config file ignoring comment lines.
298  *  Files specified one per line executable with a corresponding
299  *  pam service name.
300  */
read_pam_config(void)301 static int read_pam_config(void)
302 {
303 	const char *config_file_path = PAM_SERVICE_CONFIG;
304 	FILE *cfg = NULL;
305 	cfg = fopen(config_file_path, "r");
306 	if (!cfg)
307 		return 0;	/* This configuration is optional. */
308 	app_service_names =
309 	    hashtab_create(reqsymhash, reqsymcmp, PAM_SERVICE_SLOTS);
310 	if (!app_service_names)
311 		goto err;
312 	if (process_pam_config(cfg))
313 		goto err;
314 	fclose(cfg);
315 	return 0;
316       err:
317 	fclose(cfg);
318 	return -1;
319 }
320 
321 #else				/* else !USE_PAM */
322 
323 /************************************************************************
324  *
325  * All shadow passwd code goes in this section.
326  *
327  ************************************************************************/
328 #include <shadow.h>		/* for shadow passwd functions */
329 #include <string.h>		/* for strlen(), memset() */
330 
331 #define PASSWORD_PROMPT _("Password:")	/* prompt for getpass() */
332 
memzero(void * ptr,size_t size)333 static void memzero(void *ptr, size_t size)
334 {
335 	volatile unsigned char * volatile p = ptr;
336 	while (size--) {
337 		*p++ = '\0';
338 	}
339 }
340 
341 /* authenticate_via_shadow_passwd()
342  *
343  * in:     uname - the calling user's user name
344  * out:    nothing
345  * return: value   condition
346  *         -----   ---------
347  *           1     user authenticated themselves properly according to the
348  *                 shadow passwd file.
349  *           0     otherwise
350  *
351  * This function uses the shadow passwd file to thenticate the user running
352  * this program.
353  */
authenticate_via_shadow_passwd(const char * uname)354 static int authenticate_via_shadow_passwd(const char *uname)
355 {
356 	struct spwd *p_shadow_line;
357 	char *unencrypted_password_s;
358 	char *encrypted_password_s;
359 	int ret;
360 
361 	setspent();
362 	p_shadow_line = getspnam(uname);
363 	endspent();
364 	if (!(p_shadow_line)) {
365 		fprintf(stderr, _("Cannot find your entry in the shadow "
366 				  "passwd file.\n"));
367 		return 0;
368 	}
369 
370 	/* Ask user to input unencrypted password */
371 	if (!(unencrypted_password_s = getpass(PASSWORD_PROMPT))) {
372 		fprintf(stderr, _("getpass cannot open /dev/tty\n"));
373 		return 0;
374 	}
375 
376 	/* Use crypt() to encrypt user's input password. */
377 	errno = 0;
378 	encrypted_password_s = crypt(unencrypted_password_s,
379 				     p_shadow_line->sp_pwdp);
380 	memzero(unencrypted_password_s, strlen(unencrypted_password_s));
381 	if (errno || !encrypted_password_s) {
382 		fprintf(stderr, _("Cannot encrypt password.\n"));
383 		return 0;
384 	}
385 
386 	ret = !strcmp(encrypted_password_s, p_shadow_line->sp_pwdp);
387 	memzero(encrypted_password_s, strlen(encrypted_password_s));
388 	return ret;
389 }
390 #endif				/* if/else USE_PAM */
391 
392 /**
393  * This function checks to see if the shell is known in /etc/shells.
394  * If so, it returns 1. On error or illegal shell, it returns 0.
395  */
verify_shell(const char * shell_name)396 static int verify_shell(const char *shell_name)
397 {
398 	int found = 0;
399 	const char *buf;
400 
401 	if (!(shell_name && shell_name[0]))
402 		return found;
403 
404 	while ((buf = getusershell()) != NULL) {
405 		/* ignore comments */
406 		if (*buf == '#')
407 			continue;
408 
409 		/* check the shell skipping newline char */
410 		if (!strcmp(shell_name, buf)) {
411 			found = 1;
412 			break;
413 		}
414 	}
415 	endusershell();
416 	return found;
417 }
418 
419 /**
420  * Determine the Linux user identity to re-authenticate.
421  * If supported and set, use the login uid, as this should be more stable.
422  * Otherwise, use the real uid.
423  *
424  * This function assigns malloc'd memory into the pw_copy struct.
425  * Returns zero on success, non-zero otherwise
426  */
extract_pw_data(struct passwd * pw_copy)427 static int extract_pw_data(struct passwd *pw_copy)
428 {
429 	uid_t uid;
430 	struct passwd *pw;
431 
432 #ifdef USE_AUDIT
433 	uid = audit_getloginuid();
434 	if (uid == (uid_t) - 1)
435 		uid = getuid();
436 #else
437 	uid = getuid();
438 #endif
439 
440 	setpwent();
441 	pw = getpwuid(uid);
442 	endpwent();
443 	if (!(pw && pw->pw_name && pw->pw_name[0] && pw->pw_shell
444 	      && pw->pw_shell[0] && pw->pw_dir && pw->pw_dir[0])) {
445 		fprintf(stderr,
446 			_("cannot find valid entry in the passwd file.\n"));
447 		return -1;
448 	}
449 
450 	*pw_copy = *pw;
451 	pw = pw_copy;
452 	pw->pw_name = strdup(pw->pw_name);
453 	pw->pw_dir = strdup(pw->pw_dir);
454 	pw->pw_shell = strdup(pw->pw_shell);
455 
456 	if (!(pw->pw_name && pw->pw_dir && pw->pw_shell)) {
457 		fprintf(stderr, _("Out of memory!\n"));
458 		goto out_free;
459 	}
460 
461 	if (verify_shell(pw->pw_shell) == 0) {
462 		fprintf(stderr, _("Error!  Shell is not valid.\n"));
463 		goto out_free;
464 	}
465 	return 0;
466 
467       out_free:
468 	free(pw->pw_name);
469 	free(pw->pw_dir);
470 	free(pw->pw_shell);
471 	pw->pw_name = NULL;
472 	pw->pw_dir = NULL;
473 	pw->pw_shell = NULL;
474 	return -1;
475 }
476 
477 /**
478  * Either restore the original environment, or set up a minimal one.
479  *
480  * The minimal environment contains:
481  * TERM, DISPLAY and XAUTHORITY - if they are set, preserve values
482  * HOME, SHELL, USER and LOGNAME - set to contents of /etc/passwd
483  * PATH - set to default value DEFAULT_PATH
484  *
485  * Returns zero on success, non-zero otherwise
486  */
restore_environment(int preserve_environment,char ** old_environ,const struct passwd * pw)487 static int restore_environment(int preserve_environment,
488 			       char **old_environ, const struct passwd *pw)
489 {
490 	char const *term_env;
491 	char const *display_env;
492 	char const *xauthority_env;
493 	char *term = NULL;	/* temporary container */
494 	char *display = NULL;	/* temporary container */
495 	char *xauthority = NULL;	/* temporary container */
496 	int rc;
497 
498 	environ = old_environ;
499 
500 	if (preserve_environment)
501 		return 0;
502 
503 	term_env = getenv("TERM");
504 	display_env = getenv("DISPLAY");
505 	xauthority_env = getenv("XAUTHORITY");
506 
507 	/* Save the variable values we want */
508 	if (term_env)
509 		term = strdup(term_env);
510 	if (display_env)
511 		display = strdup(display_env);
512 	if (xauthority_env)
513 		xauthority = strdup(xauthority_env);
514 	if ((term_env && !term) || (display_env && !display) ||
515 	    (xauthority_env && !xauthority)) {
516 		rc = -1;
517 		goto out;
518 	}
519 
520 	/* Construct a new environment */
521 	if ((rc = clearenv())) {
522 		fprintf(stderr, _("Unable to clear environment\n"));
523 		goto out;
524 	}
525 
526 	/* Restore that which we saved */
527 	if (term)
528 		rc |= setenv("TERM", term, 1);
529 	if (display)
530 		rc |= setenv("DISPLAY", display, 1);
531 	if (xauthority)
532 		rc |= setenv("XAUTHORITY", xauthority, 1);
533 	rc |= setenv("HOME", pw->pw_dir, 1);
534 	rc |= setenv("SHELL", pw->pw_shell, 1);
535 	rc |= setenv("USER", pw->pw_name, 1);
536 	rc |= setenv("LOGNAME", pw->pw_name, 1);
537 	rc |= setenv("PATH", DEFAULT_PATH, 1);
538       out:
539 	free(term);
540 	free(display);
541 	free(xauthority);
542 	return rc;
543 }
544 
545 /**
546  * This function will drop the capabilities so that we are left
547  * only with access to the audit system. If the user is root, we leave
548  * the capabilities alone since they already should have access to the
549  * audit netlink socket.
550  *
551  * Returns zero on success, non-zero otherwise
552  */
553 #if defined(AUDIT_LOG_PRIV) && !defined(NAMESPACE_PRIV)
drop_capabilities(int full)554 static int drop_capabilities(int full)
555 {
556 	uid_t uid = getuid();
557 	if (!uid) return 0;
558 
559 	capng_setpid(getpid());
560 	capng_clear(CAPNG_SELECT_CAPS);
561 
562 	if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
563 		fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
564 		return -1;
565 	}
566 
567 	/* Change uid */
568 	if (setresuid(uid, uid, uid)) {
569 		fprintf(stderr, _("Error changing uid, aborting.\n"));
570 		return -1;
571 	}
572 
573 	if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) {
574 		fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
575 		return -1;
576 	}
577 
578 	if (! full)
579 		capng_update(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_AUDIT_WRITE);
580 	return capng_apply(CAPNG_SELECT_CAPS);
581 }
582 #elif defined(NAMESPACE_PRIV)
583 /**
584  * This function will drop the capabilities so that we are left
585  * only with access to the audit system and the ability to raise
586  * CAP_SYS_ADMIN, CAP_DAC_OVERRIDE, CAP_FOWNER and CAP_CHOWN,
587  * before invoking pam_namespace.  These capabilities are needed
588  * for performing bind mounts/unmounts and to create potential new
589  * instance directories with appropriate DAC attributes. If the
590  * user is root, we leave the capabilities alone since they already
591  * should have access to the audit netlink socket and should have
592  * the ability to create/mount/unmount instance directories.
593  *
594  * Returns zero on success, non-zero otherwise
595  */
drop_capabilities(int full)596 static int drop_capabilities(int full)
597 {
598 	uid_t uid = getuid();
599 	if (!uid) return 0;
600 
601 	capng_setpid(getpid());
602 	capng_clear(CAPNG_SELECT_CAPS);
603 
604 	if (prctl(PR_SET_KEEPCAPS, 1, 0, 0, 0) < 0) {
605 		fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
606 		return -1;
607 	}
608 
609 	/* Change uid */
610 	if (setresuid(uid, uid, uid)) {
611 		fprintf(stderr, _("Error changing uid, aborting.\n"));
612 		return -1;
613 	}
614 
615 	if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) {
616 		fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
617 		return -1;
618 	}
619 
620 	if (! full)
621 		capng_updatev(CAPNG_ADD, CAPNG_EFFECTIVE | CAPNG_PERMITTED, CAP_SYS_ADMIN , CAP_FOWNER , CAP_CHOWN, CAP_DAC_OVERRIDE, CAP_AUDIT_WRITE, -1);
622 
623 	return capng_apply(CAPNG_SELECT_CAPS);
624 }
625 
626 #else
drop_capabilities(int full)627 static inline int drop_capabilities(__attribute__ ((__unused__)) int full)
628 {
629 	return 0;
630 }
631 #endif
632 
633 #ifdef NAMESPACE_PRIV
634 /**
635  * This function will set the uid values to be that of caller's uid, and
636  * will drop any privilege which may have been raised.
637  */
transition_to_caller_uid(void)638 static int transition_to_caller_uid(void)
639 {
640 	uid_t uid = getuid();
641 
642 	if (prctl(PR_SET_KEEPCAPS, 0, 0, 0, 0) < 0) {
643 		fprintf(stderr, _("Error resetting KEEPCAPS, aborting\n"));
644 		return -1;
645 	}
646 
647 	if (setresuid(uid, uid, uid)) {
648 		fprintf(stderr, _("Error changing uid, aborting.\n"));
649 		return -1;
650 	}
651 	return 0;
652 }
653 #endif
654 
655 #ifdef AUDIT_LOG_PRIV
656 /* Send audit message */
657 static
send_audit_message(int success,const char * old_context,const char * new_context,const char * ttyn)658 int send_audit_message(int success, const char *old_context,
659 		       const char *new_context, const char *ttyn)
660 {
661 	char *msg = NULL;
662 	int rc;
663 	int audit_fd = audit_open();
664 
665 	if (audit_fd < 0) {
666 		fprintf(stderr, _("Error connecting to audit system.\n"));
667 		return -1;
668 	}
669 	if (asprintf(&msg, "newrole: old-context=%s new-context=%s",
670 		     old_context ? old_context : "?",
671 		     new_context ? new_context : "?") < 0) {
672 		fprintf(stderr, _("Error allocating memory.\n"));
673 		rc = -1;
674 		goto out;
675 	}
676 	rc = audit_log_user_message(audit_fd, AUDIT_USER_ROLE_CHANGE,
677 				    msg, NULL, NULL, ttyn, success);
678 	if (rc <= 0) {
679 		fprintf(stderr, _("Error sending audit message.\n"));
680 		rc = -1;
681 		goto out;
682 	}
683 	rc = 0;
684       out:
685 	free(msg);
686 	close(audit_fd);
687 	return rc;
688 }
689 #else
690 static inline
send_audit_message(int success,const char * old_context,const char * new_context,const char * ttyn)691     int send_audit_message(int success __attribute__ ((unused)),
692 			   const char *old_context
693 			   __attribute__ ((unused)),
694 			   const char *new_context
695 			   __attribute__ ((unused)), const char *ttyn
696 			   __attribute__ ((unused)))
697 {
698 	return 0;
699 }
700 #endif
701 
702 /**
703  * This function attempts to relabel the tty. If this function fails, then
704  * the fd is closed, the contexts are free'd and -1 is returned. On success,
705  * a valid fd is returned and tty_context and new_tty_context are set.
706  *
707  * This function will not fail if it can not relabel the tty when selinux is
708  * in permissive mode.
709  */
relabel_tty(const char * ttyn,const char * new_context,char ** tty_context,char ** new_tty_context)710 static int relabel_tty(const char *ttyn, const char *new_context,
711 		       char **tty_context,
712 		       char **new_tty_context)
713 {
714 	int fd, rc;
715 	int enforcing = security_getenforce();
716 	char *tty_con = NULL;
717 	char *new_tty_con = NULL;
718 
719 	if (!ttyn)
720 		return 0;
721 
722 	if (enforcing < 0) {
723 		fprintf(stderr, _("Could not determine enforcing mode.\n"));
724 		return -1;
725 	}
726 
727 	/* Re-open TTY descriptor */
728 	fd = open(ttyn, O_RDWR | O_NONBLOCK);
729 	if (fd < 0) {
730 		fprintf(stderr, _("Error!  Could not open %s.\n"), ttyn);
731 		return fd;
732 	}
733 	/* this craziness is to make sure we can't block on open and deadlock */
734 	rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
735 	if (rc) {
736 		fprintf(stderr, _("Error!  Could not clear O_NONBLOCK on %s\n"), ttyn);
737 		close(fd);
738 		return rc;
739 	}
740 
741 	if (fgetfilecon(fd, &tty_con) < 0) {
742 		fprintf(stderr, _("%s!  Could not get current context "
743 				  "for %s, not relabeling tty.\n"),
744 			enforcing ? "Error" : "Warning", ttyn);
745 		if (enforcing)
746 			goto close_fd;
747 	}
748 
749 	if (tty_con &&
750 	    (security_compute_relabel(new_context, tty_con,
751 				      string_to_security_class("chr_file"), &new_tty_con) < 0)) {
752 		fprintf(stderr, _("%s!  Could not get new context for %s, "
753 				  "not relabeling tty.\n"),
754 			enforcing ? "Error" : "Warning", ttyn);
755 		if (enforcing)
756 			goto close_fd;
757 	}
758 
759 	if (new_tty_con)
760 		if (fsetfilecon(fd, new_tty_con) < 0) {
761 			fprintf(stderr,
762 				_("%s!  Could not set new context for %s\n"),
763 				enforcing ? "Error" : "Warning", ttyn);
764 			freecon(new_tty_con);
765 			new_tty_con = NULL;
766 			if (enforcing)
767 				goto close_fd;
768 		}
769 
770 	*tty_context = tty_con;
771 	*new_tty_context = new_tty_con;
772 	return fd;
773 
774       close_fd:
775 	freecon(tty_con);
776 	close(fd);
777 	return -1;
778 }
779 
780 /**
781  * This function attempts to revert the relabeling done to the tty.
782  * fd   - referencing the opened ttyn
783  * ttyn - name of tty to restore
784  * tty_context     - original context of the tty
785  * new_tty_context - context tty was relabeled to
786  *
787  * Returns zero on success, non-zero otherwise
788  */
restore_tty_label(int fd,const char * ttyn,const char * tty_context,const char * new_tty_context)789 static int restore_tty_label(int fd, const char *ttyn,
790 			     const char *tty_context,
791 			     const char *new_tty_context)
792 {
793 	int rc = 0;
794 	char *chk_tty_context = NULL;
795 
796 	if (!ttyn)
797 		goto skip_relabel;
798 
799 	if (!new_tty_context)
800 		goto skip_relabel;
801 
802 	/* Verify that the tty still has the context set by newrole. */
803 	if ((rc = fgetfilecon(fd, &chk_tty_context)) < 0) {
804 		fprintf(stderr, "Could not fgetfilecon %s.\n", ttyn);
805 		goto skip_relabel;
806 	}
807 
808 	if ((rc = strcmp(chk_tty_context, new_tty_context))) {
809 		fprintf(stderr, _("%s changed labels.\n"), ttyn);
810 		goto skip_relabel;
811 	}
812 
813 	if ((rc = fsetfilecon(fd, tty_context)) < 0)
814 		fprintf(stderr,
815 			_("Warning! Could not restore context for %s\n"), ttyn);
816       skip_relabel:
817 	freecon(chk_tty_context);
818 	return rc;
819 }
820 
821 /**
822  * Parses and validates the provided command line options and
823  * constructs a new context based on our old context and the
824  * arguments specified on the command line. On success
825  * new_context will be set to valid values, otherwise its value
826  * is left unchanged.
827  *
828  * Returns zero on success, non-zero otherwise.
829  */
parse_command_line_arguments(int argc,char ** argv,char * ttyn,const char * old_context,char ** new_context,int * preserve_environment)830 static int parse_command_line_arguments(int argc, char **argv, char *ttyn,
831 					const char *old_context,
832 					char **new_context,
833 					int *preserve_environment)
834 {
835 	int flag_index;		/* flag index in argv[] */
836 	int clflag;		/* holds codes for command line flags */
837 	char *role_s = NULL;	/* role spec'd by user in argv[] */
838 	char *type_s = NULL;	/* type spec'd by user in argv[] */
839 	char *type_ptr = NULL;	/* stores malloc'd data from get_default_type */
840 	char *level_s = NULL;	/* level spec'd by user in argv[] */
841 	char *range_ptr = NULL;
842 	const char *new_con = NULL;
843 	char *tty_con = NULL;
844 	context_t context = NULL;	/* manipulatable form of new_context */
845 	const struct option long_options[] = {
846 		{"role", 1, 0, 'r'},
847 		{"type", 1, 0, 't'},
848 		{"level", 1, 0, 'l'},
849 		{"preserve-environment", 0, 0, 'p'},
850 		{"version", 0, 0, 'V'},
851 		{NULL, 0, 0, 0}
852 	};
853 
854 	*preserve_environment = 0;
855 	while (1) {
856 		clflag = getopt_long(argc, argv, "r:t:l:pV", long_options,
857 				     &flag_index);
858 		if (clflag == -1)
859 			break;
860 
861 		switch (clflag) {
862 		case 'V':
863 			printf("newrole: %s version %s\n", PACKAGE, VERSION);
864 			exit(0);
865 		case 'p':
866 			*preserve_environment = 1;
867 			break;
868 		case 'r':
869 			if (role_s) {
870 				fprintf(stderr,
871 					_("Error: multiple roles specified\n"));
872 				return -1;
873 			}
874 			role_s = optarg;
875 			break;
876 		case 't':
877 			if (type_s) {
878 				fprintf(stderr,
879 					_("Error: multiple types specified\n"));
880 				return -1;
881 			}
882 			type_s = optarg;
883 			break;
884 		case 'l':
885 			if (!is_selinux_mls_enabled()) {
886 				fprintf(stderr, _("Sorry, -l may be used with "
887 						  "SELinux MLS support.\n"));
888 				return -1;
889 			}
890 			if (level_s) {
891 				fprintf(stderr, _("Error: multiple levels "
892 						  "specified\n"));
893 				return -1;
894 			}
895 			if (ttyn) {
896 				if (fgetfilecon(STDIN_FILENO, &tty_con) >= 0) {
897 					if (selinux_check_securetty_context
898 					    (tty_con) < 0) {
899 						fprintf(stderr,
900 							_
901 							("Error: you are not allowed to change levels on a non secure terminal \n"));
902 						freecon(tty_con);
903 						return -1;
904 					}
905 					freecon(tty_con);
906 				}
907 			}
908 
909 			level_s = optarg;
910 			break;
911 		default:
912 			fprintf(stderr, "%s\n", USAGE_STRING);
913 			return -1;
914 		}
915 	}
916 
917 	/* Verify that the combination of command-line arguments are viable */
918 	if (!(role_s || type_s || level_s)) {
919 		fprintf(stderr, "%s\n", USAGE_STRING);
920 		return -1;
921 	}
922 
923 	/* Fill in a default type if one hasn't been specified. */
924 	if (role_s && !type_s) {
925 		/* get_default_type() returns malloc'd memory */
926 		if (get_default_type(role_s, &type_ptr)) {
927 			fprintf(stderr, _("Couldn't get default type.\n"));
928 			send_audit_message(0, old_context, new_con, ttyn);
929 			return -1;
930 		}
931 		type_s = type_ptr;
932 	}
933 
934 	/* Create a temporary new context structure we extract and modify */
935 	context = context_new(old_context);
936 	if (!context) {
937 		fprintf(stderr, _("failed to get new context.\n"));
938 		goto err_free;
939 	}
940 
941 	/* Modify the temporary new context */
942 	if (role_s)
943 		if (context_role_set(context, role_s)) {
944 			fprintf(stderr, _("failed to set new role %s\n"),
945 				role_s);
946 			goto err_free;
947 		}
948 
949 	if (type_s)
950 		if (context_type_set(context, type_s)) {
951 			fprintf(stderr, _("failed to set new type %s\n"),
952 				type_s);
953 			goto err_free;
954 		}
955 
956 	if (level_s) {
957 		range_ptr =
958 		    build_new_range(level_s, context_range_get(context));
959 		if (!range_ptr) {
960 			fprintf(stderr,
961 				_("failed to build new range with level %s\n"),
962 				level_s);
963 			goto err_free;
964 		}
965 		if (context_range_set(context, range_ptr)) {
966 			fprintf(stderr, _("failed to set new range %s\n"),
967 				range_ptr);
968 			goto err_free;
969 		}
970 	}
971 
972 	/* Construct the final new context */
973 	if (!(new_con = context_str(context))) {
974 		fprintf(stderr, _("failed to convert new context to string\n"));
975 		goto err_free;
976 	}
977 
978 	if (security_check_context(new_con) < 0) {
979 		fprintf(stderr, _("%s is not a valid context\n"), new_con);
980 		send_audit_message(0, old_context, new_con, ttyn);
981 		goto err_free;
982 	}
983 
984 	*new_context = strdup(new_con);
985 	if (!*new_context) {
986 		fprintf(stderr, _("Unable to allocate memory for new_context"));
987 		goto err_free;
988 	}
989 
990 	free(type_ptr);
991 	free(range_ptr);
992 	context_free(context);
993 	return 0;
994 
995       err_free:
996 	free(type_ptr);
997 	free(range_ptr);
998 	/* Don't free new_con, context_free(context) handles this */
999 	context_free(context);
1000 	return -1;
1001 }
1002 
1003 /**
1004  * Take care of any signal setup
1005  */
set_signal_handles(void)1006 static int set_signal_handles(void)
1007 {
1008 	sigset_t empty;
1009 
1010 	/* Empty the signal mask in case someone is blocking a signal */
1011 	if (sigemptyset(&empty)) {
1012 		fprintf(stderr, _("Unable to obtain empty signal set\n"));
1013 		return -1;
1014 	}
1015 
1016 	(void)sigprocmask(SIG_SETMASK, &empty, NULL);
1017 
1018 	/* Terminate on SIGHUP. */
1019 	if (signal(SIGHUP, SIG_DFL) == SIG_ERR) {
1020 		fprintf(stderr, _("Unable to set SIGHUP handler\n"));
1021 		return -1;
1022 	}
1023 
1024 	return 0;
1025 }
1026 
1027 /************************************************************************
1028  *
1029  * All code used for both PAM and shadow passwd goes in this section.
1030  *
1031  ************************************************************************/
1032 
main(int argc,char * argv[])1033 int main(int argc, char *argv[])
1034 {
1035 	char *new_context = NULL;	/* target security context */
1036 	char *old_context = NULL;	/* original security context */
1037 	char *tty_context = NULL;	/* current context of tty */
1038 	char *new_tty_context = NULL;	/* new context of tty */
1039 
1040 	struct passwd pw;	/* struct derived from passwd file line */
1041 	char *ttyn = NULL;	/* tty path */
1042 
1043 	char **old_environ;
1044 	int preserve_environment;
1045 
1046 	int fd;
1047 	pid_t childPid = 0;
1048 	char *shell_argv0 = NULL;
1049 	int rc;
1050 
1051 #ifdef USE_PAM
1052 	int pam_status;		/* pam return code */
1053 	pam_handle_t *pam_handle;	/* opaque handle used by all PAM functions */
1054 
1055 	/* This is a jump table of functions for PAM to use when it wants to *
1056 	 * communicate with the user.  We'll be using misc_conv(), which is  *
1057 	 * provided for us via pam_misc.h.                                   */
1058 	struct pam_conv pam_conversation = {
1059 		misc_conv,
1060 		NULL
1061 	};
1062 #endif
1063 
1064 	/*
1065 	 * Step 0: Setup
1066 	 *
1067 	 * Do some initial setup, including dropping capabilities, checking
1068 	 * if it makes sense to continue to run newrole, and setting up
1069 	 * a scrubbed environment.
1070 	 */
1071 	if (drop_capabilities(FALSE)) {
1072 		perror(_("Sorry, newrole failed to drop capabilities\n"));
1073 		return -1;
1074 	}
1075 	if (set_signal_handles())
1076 		return -1;
1077 
1078 #ifdef USE_NLS
1079 	setlocale(LC_ALL, "");
1080 	bindtextdomain(PACKAGE, LOCALEDIR);
1081 	textdomain(PACKAGE);
1082 #endif
1083 
1084 	old_environ = environ;
1085 	environ = NULL;
1086 
1087 	if (!is_selinux_enabled()) {
1088 		fprintf(stderr, _("Sorry, newrole may be used only on "
1089 				  "a SELinux kernel.\n"));
1090 		return -1;
1091 	}
1092 
1093 	if (security_getenforce() < 0) {
1094 		fprintf(stderr, _("Could not determine enforcing mode.\n"));
1095 		return -1;
1096 	}
1097 
1098 	/*
1099 	 * Step 1: Parse command line and valid arguments
1100 	 *
1101 	 * old_context and ttyn are required for audit logging,
1102 	 * context validation and pam
1103 	 */
1104 	if (getprevcon(&old_context)) {
1105 		fprintf(stderr, _("failed to get old_context.\n"));
1106 		return -1;
1107 	}
1108 
1109 	ttyn = ttyname(STDIN_FILENO);
1110 	if (!ttyn || *ttyn == '\0') {
1111 		fprintf(stderr,
1112 			_("Warning!  Could not retrieve tty information.\n"));
1113 	}
1114 
1115 	if (parse_command_line_arguments(argc, argv, ttyn, old_context,
1116 					 &new_context, &preserve_environment))
1117 		return -1;
1118 
1119 	/*
1120 	 * Step 2:  Authenticate the user.
1121 	 *
1122 	 * Re-authenticate the user running this program.
1123 	 * This is just to help confirm user intent (vs. invocation by
1124 	 * malicious software), not to authorize the operation (which is covered
1125 	 * by policy).  Trusted path mechanism would be preferred.
1126 	 */
1127 	memset(&pw, 0, sizeof(pw));
1128 	if (extract_pw_data(&pw))
1129 		goto err_free;
1130 
1131 #ifdef USE_PAM
1132 	if (read_pam_config()) {
1133 		fprintf(stderr,
1134 			_("error on reading PAM service configuration.\n"));
1135 		goto err_free;
1136 	}
1137 
1138 	if (app_service_names != NULL && optind < argc) {
1139 		if (strcmp(argv[optind], "-c") == 0 && optind < (argc - 1)) {
1140 			/*
1141 			 * Check for a separate pam service name for the
1142 			 * command when invoked by newrole.
1143 			 */
1144 			char *cmd = NULL;
1145 			rc = sscanf(argv[optind + 1], "%ms", &cmd);
1146 			if (rc != EOF && cmd) {
1147 				char *app_service_name =
1148 				    (char *)hashtab_search(app_service_names,
1149 							   cmd);
1150 				free(cmd);
1151 				if (app_service_name != NULL)
1152 					service_name = app_service_name;
1153 			}
1154 		}
1155 	}
1156 
1157 	pam_status = pam_start(service_name, pw.pw_name, &pam_conversation,
1158 			       &pam_handle);
1159 	if (pam_status != PAM_SUCCESS) {
1160 		fprintf(stderr, _("failed to initialize PAM\n"));
1161 		goto err_free;
1162 	}
1163 
1164 	if (!authenticate_via_pam(ttyn, pam_handle))
1165 #else
1166 	if (!authenticate_via_shadow_passwd(pw.pw_name))
1167 #endif
1168 	{
1169 		fprintf(stderr, _("newrole: incorrect password for %s\n"),
1170 			pw.pw_name);
1171 		send_audit_message(0, old_context, new_context, ttyn);
1172 		goto err_close_pam;
1173 	}
1174 
1175 	/*
1176 	 * Step 3:  Handle relabeling of the tty.
1177 	 *
1178 	 * Once we authenticate the user, we know that we want to proceed with
1179 	 * the action. Prior to this point, no changes are made the to system.
1180 	 */
1181 	fd = relabel_tty(ttyn, new_context, &tty_context, &new_tty_context);
1182 	if (fd < 0)
1183 		goto err_close_pam;
1184 
1185 	/*
1186 	 * Step 4: Fork
1187 	 *
1188 	 * Fork, allowing parent to clean up after shell has executed.
1189 	 * Child: reopen stdin, stdout, stderr and exec shell
1190 	 * Parnet: wait for child to die and restore tty's context
1191 	 */
1192 	childPid = fork();
1193 	if (childPid < 0) {
1194 		/* fork failed, no child to worry about */
1195 		int errsv = errno;
1196 		fprintf(stderr, _("newrole: failure forking: %s"),
1197 			strerror(errsv));
1198 		if (restore_tty_label(fd, ttyn, tty_context, new_tty_context))
1199 			fprintf(stderr, _("Unable to restore tty label...\n"));
1200 		if (close(fd))
1201 			fprintf(stderr, _("Failed to close tty properly\n"));
1202 		goto err_close_pam;
1203 	} else if (childPid) {
1204 		/* PARENT
1205 		 * It doesn't make senes to exit early on errors at this point,
1206 		 * since we are doing cleanup which needs to be done.
1207 		 * We can exit with a bad rc though
1208 		 */
1209 		pid_t pid;
1210 		int exit_code = 0;
1211 		int status;
1212 
1213 		do {
1214 			pid = wait(&status);
1215 		} while (pid < 0 && errno == EINTR);
1216 
1217 		/* Preserve child exit status, unless there is another error. */
1218 		if (WIFEXITED(status))
1219 			exit_code = WEXITSTATUS(status);
1220 
1221 		if (restore_tty_label(fd, ttyn, tty_context, new_tty_context)) {
1222 			fprintf(stderr, _("Unable to restore tty label...\n"));
1223 			exit_code = -1;
1224 		}
1225 		freecon(tty_context);
1226 		freecon(new_tty_context);
1227 		if (close(fd)) {
1228 			fprintf(stderr, _("Failed to close tty properly\n"));
1229 			exit_code = -1;
1230 		}
1231 #ifdef USE_PAM
1232 #ifdef NAMESPACE_PRIV
1233 		pam_status = pam_close_session(pam_handle, 0);
1234 		if (pam_status != PAM_SUCCESS) {
1235 			fprintf(stderr, "pam_close_session failed with %s\n",
1236 				pam_strerror(pam_handle, pam_status));
1237 			exit_code = -1;
1238 		}
1239 #endif
1240 		rc = pam_end(pam_handle, pam_status);
1241 		if (rc != PAM_SUCCESS) {
1242 			fprintf(stderr, "pam_end failed with %s\n",
1243 				pam_strerror(pam_handle, rc));
1244 			exit_code = -1;
1245 		}
1246 		hashtab_map(app_service_names, free_hashtab_entry, NULL);
1247 		hashtab_destroy(app_service_names);
1248 #endif
1249 		free(pw.pw_name);
1250 		free(pw.pw_dir);
1251 		free(pw.pw_shell);
1252 		free(shell_argv0);
1253 		free(new_context);
1254 		return exit_code;
1255 	}
1256 
1257 	/* CHILD */
1258 	/* Close the tty and reopen descriptors 0 through 2 */
1259 	if (ttyn) {
1260 		if (close(fd) || close(0) || close(1) || close(2)) {
1261 			fprintf(stderr, _("Could not close descriptors.\n"));
1262 			goto err_close_pam;
1263 		}
1264 		fd = open(ttyn, O_RDWR | O_NONBLOCK);
1265 		if (fd != 0)
1266 			goto err_close_pam;
1267 		rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1268 		if (rc)
1269 			goto err_close_pam;
1270 
1271 		fd = open(ttyn, O_RDWR | O_NONBLOCK);
1272 		if (fd != 1)
1273 			goto err_close_pam;
1274 		rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1275 		if (rc)
1276 			goto err_close_pam;
1277 
1278 		fd = open(ttyn, O_RDWR | O_NONBLOCK);
1279 		if (fd != 2)
1280 			goto err_close_pam;
1281 		rc = fcntl(fd, F_SETFL, fcntl(fd, F_GETFL, 0) & ~O_NONBLOCK);
1282 		if (rc)
1283 			goto err_close_pam;
1284 
1285 	}
1286 	/*
1287 	 * Step 5:  Execute a new shell with the new context in `new_context'.
1288 	 *
1289 	 * Establish context, namespace and any options for the new shell
1290 	 */
1291 	if (optind < 1)
1292 		optind = 1;
1293 
1294 	/* This is ugly, but use newrole's argv for the exec'd shells argv */
1295 	if (asprintf(&shell_argv0, "-%s", pw.pw_shell) < 0) {
1296 		fprintf(stderr, _("Error allocating shell's argv0.\n"));
1297 		shell_argv0 = NULL;
1298 		goto err_close_pam;
1299 	}
1300 	argv[optind - 1] = shell_argv0;
1301 
1302 	if (setexeccon(new_context)) {
1303 		fprintf(stderr, _("Could not set exec context to %s.\n"),
1304 			new_context);
1305 		goto err_close_pam;
1306 	}
1307 #ifdef NAMESPACE_PRIV
1308 	/* Ask PAM to setup session for user running this program */
1309 	pam_status = pam_open_session(pam_handle, 0);
1310 	if (pam_status != PAM_SUCCESS) {
1311 		fprintf(stderr, "pam_open_session failed with %s\n",
1312 			pam_strerror(pam_handle, pam_status));
1313 		goto err_close_pam;
1314 	}
1315 #endif
1316 
1317 	if (send_audit_message(1, old_context, new_context, ttyn)) {
1318 		fprintf(stderr, _("Failed to send audit message"));
1319 		goto err_close_pam_session;
1320 	}
1321 	freecon(old_context); old_context=NULL;
1322 	freecon(new_context); new_context=NULL;
1323 
1324 #ifdef NAMESPACE_PRIV
1325 	if (transition_to_caller_uid()) {
1326 		fprintf(stderr, _("Failed to transition to namespace\n"));
1327 		goto err_close_pam_session;
1328 	}
1329 #endif
1330 
1331 	if (drop_capabilities(TRUE)) {
1332 		fprintf(stderr, _("Failed to drop capabilities %m\n"));
1333 		goto err_close_pam_session;
1334 	}
1335 	/* Handle environment changes */
1336 	if (restore_environment(preserve_environment, old_environ, &pw)) {
1337 		fprintf(stderr, _("Unable to restore the environment, "
1338 				  "aborting\n"));
1339 		goto err_close_pam_session;
1340 	}
1341 	execv(pw.pw_shell, argv + optind - 1);
1342 
1343 	/*
1344 	 * Error path cleanup
1345 	 *
1346 	 * If we reach here, then we failed to exec the new shell.
1347 	 */
1348 	perror(_("failed to exec shell\n"));
1349       err_close_pam_session:
1350 #ifdef NAMESPACE_PRIV
1351 	pam_status = pam_close_session(pam_handle, 0);
1352 	if (pam_status != PAM_SUCCESS)
1353 		fprintf(stderr, "pam_close_session failed with %s\n",
1354 			pam_strerror(pam_handle, pam_status));
1355 #endif
1356       err_close_pam:
1357 #ifdef USE_PAM
1358 	rc = pam_end(pam_handle, pam_status);
1359 	if (rc != PAM_SUCCESS)
1360 		fprintf(stderr, "pam_end failed with %s\n",
1361 			pam_strerror(pam_handle, rc));
1362 #endif
1363       err_free:
1364 	freecon(tty_context);
1365 	freecon(new_tty_context);
1366 	freecon(old_context);
1367 	freecon(new_context);
1368 	free(pw.pw_name);
1369 	free(pw.pw_dir);
1370 	free(pw.pw_shell);
1371 	free(shell_argv0);
1372 #ifdef USE_PAM
1373 	if (app_service_names) {
1374 		hashtab_map(app_service_names, free_hashtab_entry, NULL);
1375 		hashtab_destroy(app_service_names);
1376 	}
1377 #endif
1378 	return -1;
1379 }				/* main() */
1380