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