[launchd-changes] [23880] trunk/launchd/src

source_changes at macosforge.org source_changes at macosforge.org
Thu Apr 2 18:35:20 PDT 2009


Revision: 23880
          http://trac.macosforge.org/projects/launchd/changeset/23880
Author:   dsorresso at apple.com
Date:     2009-04-02 18:35:19 -0700 (Thu, 02 Apr 2009)
Log Message:
-----------
Embedded security changes.

Modified Paths:
--------------
    trunk/launchd/src/launch_priv.h
    trunk/launchd/src/launchd_core_logic.c
    trunk/launchd/src/launchd_core_logic.h
    trunk/launchd/src/launchd_unix_ipc.c
    trunk/launchd/src/liblaunch.c
    trunk/launchd/src/vproc_priv.h

Modified: trunk/launchd/src/launch_priv.h
===================================================================
--- trunk/launchd/src/launch_priv.h	2009-04-02 22:41:10 UTC (rev 23879)
+++ trunk/launchd/src/launch_priv.h	2009-04-03 01:35:19 UTC (rev 23880)
@@ -62,6 +62,7 @@
 #define LAUNCH_JOBKEY_SECURITYSESSIONUUID				"SecuritySessionUUID"
 
 #define LAUNCH_JOBKEY_EMBEDDEDSHUTDOWNAUTHORITY			"EmbeddedShutdownAuthority"
+#define LAUNCH_JOBKEY_EMBEDDEDPRIVILEGEDISPENSATION		"EmbeddedPrivilegeDispensation"
 
 #define LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL		"EnterKernelDebuggerBeforeKill"
 #define LAUNCH_JOBKEY_PERJOBMACHSERVICES				"PerJobMachServices"

Modified: trunk/launchd/src/launchd_core_logic.c
===================================================================
--- trunk/launchd/src/launchd_core_logic.c	2009-04-02 22:41:10 UTC (rev 23879)
+++ trunk/launchd/src/launchd_core_logic.c	2009-04-03 01:35:19 UTC (rev 23880)
@@ -528,7 +528,7 @@
 			is_bootstrapper				:1, /* The job is a bootstrapper. */
 			has_console					:1, /* The job owns the console. */
 			clean_exit_timer_expired	:1, /* The job was clean, received SIGKILL and failed to exit after LAUNCHD_CLEAN_KILL_TIMER seconds. */
-			embedded_shutdown_auth		:1, /* The job is allowed to call reboot2() on embedded. */
+			embedded_special_privileges	:1, /* The job runs as a non-root user on embedded but has select privileges of the root user. */
 			migratory					:1; /* The (anonymous) job called vprocmgr_switch_to_session(). */
 	mode_t mask;
 	pid_t tracing_pid;
@@ -637,13 +637,15 @@
 static job_t workaround_5477111;
 static LIST_HEAD(, job_s) s_needing_sessions;
 mach_port_t g_audit_session_port = MACH_PORT_NULL;
+
 #if !TARGET_OS_EMBEDDED
+static job_t s_embedded_privileged_job = (job_t)&root_jobmgr;
 au_asid_t g_audit_session = AU_DEFAUDITSID;
 #else
+static job_t s_embedded_privileged_job = NULL;
 pid_t g_audit_session = 0;
 #endif
-static bool s_embedded_shutdown_right_claimed = false;
-static pid_t s_update_pid = 0;
+
 static int s_no_hang_fd = -1;
 
 /* process wide globals */
@@ -651,6 +653,7 @@
 jobmgr_t root_jobmgr;
 bool g_shutdown_debugging = false;
 bool g_verbose_boot = false;
+bool g_embedded_privileged_action = false;
 
 void
 job_ignore(job_t j)
@@ -741,6 +744,18 @@
 	}
 #endif
 
+#if TARGET_OS_EMBEDDED
+	if( g_embedded_privileged_action && s_embedded_privileged_job ) {
+		if( strcmp(j->username, s_embedded_privileged_job->username) != 0 ) {
+			errno = EPERM;
+			return;
+		}
+	} else if( g_embedded_privileged_action ) {
+		errno = EINVAL;
+		return;
+	}
+#endif
+
 	j->sent_signal_time = runtime_get_opaque_time();
 
 	if (newval < 0) {
@@ -1016,6 +1031,18 @@
 	struct limititem *li;
 	struct envitem *ei;
 	
+#if TARGET_OS_EMBEDDED
+	if( g_embedded_privileged_action && s_embedded_privileged_job ) {
+		if( strcmp(j->username, s_embedded_privileged_job->username) != 0 ) {
+			errno = EPERM;
+			return;
+		}
+	} else if( g_embedded_privileged_action ) {
+		errno = EINVAL;
+		return;
+	}
+#endif
+	
 	if (unlikely(j->p)) {
 		if (j->anonymous) {
 			job_reap(j);
@@ -1156,6 +1183,9 @@
 	if( !uuid_is_null(j->expected_audit_uuid) ) {
 		LIST_REMOVE(j, needing_session_sle);
 	}
+	if( j->embedded_special_privileges ) {
+		s_embedded_privileged_job = NULL;
+	}
 	
 	kevent_mod((uintptr_t)j, EVFILT_TIMER, EV_DELETE, 0, 0, NULL);
 	
@@ -1683,9 +1713,14 @@
 		} else if (strcasecmp(key, LAUNCH_JOBKEY_ENTERKERNELDEBUGGERBEFOREKILL) == 0) {
 			j->debug_before_kill = value;
 			found_key = true;
-		} else if( !s_embedded_shutdown_right_claimed && strcasecmp(key, LAUNCH_JOBKEY_EMBEDDEDSHUTDOWNAUTHORITY) == 0 ) {
-			j->embedded_shutdown_auth = true;
-			s_embedded_shutdown_right_claimed = true;
+		} else if( strcasecmp(key, LAUNCH_JOBKEY_EMBEDDEDPRIVILEGEDISPENSATION) == 0 ) {
+			if( !s_embedded_privileged_job ) {
+				j->embedded_special_privileges = value;
+				s_embedded_privileged_job = j;
+			} else {
+				job_log(j, LOG_ERR, "Job tried to claim %s after it has already been claimed.", key);
+			}
+			found_key = true;
 		}
 		break;
 	case 'w':
@@ -2177,6 +2212,31 @@
 		return NULL;
 	}
 
+#if TARGET_OS_EMBEDDED
+	if( unlikely(g_embedded_privileged_action && s_embedded_privileged_job) ) {
+		if( unlikely(!(tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_USERNAME))) ) {
+			errno = EPERM;
+			return NULL;
+		}
+		
+		const char *username = NULL;
+		if( likely(tmp && launch_data_get_type(tmp) == LAUNCH_DATA_STRING) ) {
+			username = launch_data_get_string(tmp);
+		} else {
+			errno = EPERM;
+			return NULL;
+		}
+		
+		if( unlikely(strcmp(s_embedded_privileged_job->username, username) != 0) ) {
+			errno = EPERM;
+			return NULL;
+		}
+	} else if( g_embedded_privileged_action ) {
+		errno = EINVAL;
+		return NULL;
+	}
+#endif
+
 	if ((tmp = launch_data_dict_lookup(pload, LAUNCH_JOBKEY_PROGRAM)) &&
 			(launch_data_get_type(tmp) == LAUNCH_DATA_STRING)) {
 		prog = launch_data_get_string(tmp);
@@ -2886,6 +2946,18 @@
 	if( !uuid_is_null(j->expected_audit_uuid) ) {
 		return NULL;
 	}
+
+#if TARGET_OS_EMBEDDED
+	if( g_embedded_privileged_action && s_embedded_privileged_job ) {
+		if( strcmp(j->username, s_embedded_privileged_job->username) != 0 ) {
+			errno = EPERM;
+			return NULL;
+		}
+	} else if( g_embedded_privileged_action ) {
+		errno = EINVAL;
+		return NULL;
+	}
+#endif
 	
 	/*
 	 * The whole job removal logic needs to be consolidated. The fact that
@@ -3099,14 +3171,6 @@
 	log_kevent_struct(LOG_DEBUG, kev, 0);
 	
 	if( fflags & NOTE_EXIT ) {
-		if( s_update_pid == (pid_t)kev->ident ) {
-			int status = 0;
-			job_assumes(j, waitpid(s_update_pid, &status, 0) == 0);
-			job_log(j, LOG_NOTICE, "Reaping update job (PID %i, exit status %i)", s_update_pid, WEXITSTATUS(status));
-			
-			s_update_pid = 0;
-		}
-		
 		if( j->p == (pid_t)kev->ident && !j->anonymous && !j->is_being_sampled ) {
 			int mib[] = { CTL_KERN, KERN_PROC, KERN_PROC_PID, j->p };
 			struct kinfo_proc kp;
@@ -3468,10 +3532,10 @@
 	}
 	
 	if (likely(!j->legacy_mach_job)) {
-		sipc = ( !SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices) ) && !j->deny_job_creation;
+		sipc = ((!SLIST_EMPTY(&j->sockets) || !SLIST_EMPTY(&j->machservices)) && !j->deny_job_creation) || j->embedded_special_privileges;
 	}
 
-	if (sipc) {
+	if( sipc ) {
 		job_assumes(j, socketpair(AF_UNIX, SOCK_STREAM, 0, spair) != -1);
 	}
 	
@@ -6101,6 +6165,11 @@
 	j->checkedin = true;
 }
 
+bool job_is_god(job_t j)
+{
+	return j->embedded_special_privileges;
+}
+
 bool
 job_ack_port_destruction(mach_port_t p)
 {
@@ -6612,7 +6681,17 @@
 	}
 
 	if( unlikely(ldc->euid != 0 && ldc->euid != getuid()) || j->deny_job_creation ) {
+
+	}
+
+	if( unlikely(ldc->euid != 0 && ldc->euid != getuid()) || j->deny_job_creation ) {
+	#if TARGET_OS_EMBEDDED	
+		if( !j->embedded_special_privileges ) {
+			return BOOTSTRAP_NOT_PRIVILEGED;
+		}
+	#else
 		return BOOTSTRAP_NOT_PRIVILEGED;
+	#endif
 	}
 
 #if HAVE_SANDBOX
@@ -6625,6 +6704,12 @@
 		return BOOTSTRAP_UNKNOWN_SERVICE;
 	}
 
+#if TARGET_OS_EMBEDDED
+	if( j->embedded_special_privileges && strcmp(j->username, otherj->username) != 0 ) {
+		return BOOTSTRAP_NOT_PRIVILEGED;
+	}
+#endif	
+
 	if (sig == VPROC_MAGIC_UNLOAD_SIGNAL) {
 		bool do_block = otherj->p;
 
@@ -6913,6 +6998,9 @@
 	case VPROC_GSK_WAITFORDEBUGGER:
 		*outval = j->wait4debugger;
 		break;
+	case VPROC_GSK_EMBEDDEDROOTEQUIVALENT:
+		*outval = j->embedded_special_privileges;
+		break;
 	case 0:
 		*outval = 0;
 		break;
@@ -7136,7 +7224,7 @@
 #if !TARGET_OS_EMBEDDED
 	if (unlikely(ldc->euid)) {
 #else
-	if( unlikely(ldc->euid) && !j->embedded_shutdown_auth ) {
+	if( unlikely(ldc->euid) && !j->embedded_special_privileges ) {
 #endif
 		return BOOTSTRAP_NOT_PRIVILEGED;
 	}
@@ -8356,12 +8444,12 @@
 	}
 
 #if TARGET_OS_EMBEDDED
-	bool embedded_check = j->username && otherj->username && ( strcmp(j->username, otherj->username) != 0 );
+	bool allow_non_root_kickstart = j->username && otherj->username && ( strcmp(j->username, otherj->username) == 0 );
 #else
-	bool embedded_check = true;
+	bool allow_non_root_kickstart = false;
 #endif
 
-	if( ldc->euid != 0 && ldc->euid != geteuid() && embedded_check ) {
+	if( ldc->euid != 0 && ldc->euid != geteuid() && !allow_non_root_kickstart ) {
 		return BOOTSTRAP_NOT_PRIVILEGED;
 	}
 
@@ -8741,6 +8829,10 @@
 #else
 	/* Since this is for embedded, we can assume that the root job manager holds the Jetsam jobs. */
 	jm = root_jobmgr;
+	
+	if( !g_embedded_privileged_action ) {
+		return EPERM;
+	}
 #endif
 		
 	size_t npris = launch_data_array_get_count(priorities);

Modified: trunk/launchd/src/launchd_core_logic.h
===================================================================
--- trunk/launchd/src/launchd_core_logic.h	2009-04-02 22:41:10 UTC (rev 23879)
+++ trunk/launchd/src/launchd_core_logic.h	2009-04-03 01:35:19 UTC (rev 23880)
@@ -32,6 +32,7 @@
 extern mach_port_t g_audit_session_port;
 extern au_asid_t g_audit_session;
 extern bool g_flat_mach_namespace;
+extern bool g_embedded_privileged_action;
 
 void jobmgr_init(bool);
 jobmgr_t jobmgr_shutdown(jobmgr_t jm);
@@ -50,6 +51,7 @@
 void job_stop(job_t j);
 void job_checkin(job_t j);
 void job_remove(job_t j);
+bool job_is_god(job_t j);
 job_t job_import(launch_data_t pload);
 launch_data_t job_import_bulk(launch_data_t pload);
 job_t job_mig_intran(mach_port_t mp);

Modified: trunk/launchd/src/launchd_unix_ipc.c
===================================================================
--- trunk/launchd/src/launchd_unix_ipc.c	2009-04-02 22:41:10 UTC (rev 23879)
+++ trunk/launchd/src/launchd_unix_ipc.c	2009-04-03 01:35:19 UTC (rev 23880)
@@ -354,16 +354,24 @@
 	}
 
 //	job_log(rmc->c->j, LOG_NOTICE, "Socket IPC request: %s.", cmd);
+
+	/* Do not allow commands other than check-in to come over the trusted socket
+	 * on the Desktop. On Embedded, allow all commands over the trusted socket if
+	 * the job has the God Mode key set.
+	 */
+#if TARGET_OS_EMBEDDED
+	bool allow_privileged_ops = ( !rmc->c->j || job_is_god(rmc->c->j) );
+#else
+	bool allow_privileged_ops = !rmc->c->j;
+#endif
 	
-	/* Do not allow commands other than check-in to come over the trusted socket. */
-	if( data == NULL && rmc->c->j ) {
-		if( strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0 ) {
-			resp = job_export(rmc->c->j);
-			job_checkin(rmc->c->j);
-		} else {
-			resp = launch_data_new_errno(EACCES);
-		}
-	} else {
+	if( rmc->c->j && strcmp(cmd, LAUNCH_KEY_CHECKIN) == 0 ) {
+		resp = job_export(rmc->c->j);
+		job_checkin(rmc->c->j);
+	} else if( allow_privileged_ops ) {
+	#if TARGET_OS_EMBEDDED
+		g_embedded_privileged_action = rmc->c->j && job_is_god(rmc->c->j);
+	#endif
 		if( data == NULL ) {
 			if (!strcmp(cmd, LAUNCH_KEY_SHUTDOWN)) {
 				launchd_shutdown();
@@ -388,20 +396,20 @@
 		} else {
 			if (!strcmp(cmd, LAUNCH_KEY_STARTJOB)) {
 				if ((j = job_find(launch_data_get_string(data))) != NULL) {
+					errno = 0;
 					job_dispatch(j, true);
-					errno = 0;
 				}
 				resp = launch_data_new_errno(errno);
 			} else if (!strcmp(cmd, LAUNCH_KEY_STOPJOB)) {
 				if ((j = job_find(launch_data_get_string(data))) != NULL) {
+					errno = 0;
 					job_stop(j);
-					errno = 0;
 				}
 				resp = launch_data_new_errno(errno);
 			} else if (!strcmp(cmd, LAUNCH_KEY_REMOVEJOB)) {
 				if ((j = job_find(launch_data_get_string(data))) != NULL) {
+					errno = 0;
 					job_remove(j);
-					errno = 0;
 				}
 				resp = launch_data_new_errno(errno);
 			} else if (!strcmp(cmd, LAUNCH_KEY_SUBMITJOB)) {
@@ -432,6 +440,11 @@
 				resp = launch_data_new_errno(launchd_set_jetsam_priorities(data));
 			}
 		}
+	#if TARGET_OS_EMBEDDED
+		g_embedded_privileged_action = false;
+	#endif
+	} else {
+		resp = launch_data_new_errno(EACCES);
 	}
 
 	rmc->resp = resp;

Modified: trunk/launchd/src/liblaunch.c
===================================================================
--- trunk/launchd/src/liblaunch.c	2009-04-02 22:41:10 UTC (rev 23879)
+++ trunk/launchd/src/liblaunch.c	2009-04-03 01:35:19 UTC (rev 23880)
@@ -172,6 +172,7 @@
 static launch_data_t launch_msg_internal(launch_data_t d);
 static void launch_mach_checkin_service(launch_data_t obj, const char *key, void *context);
 
+static int64_t s_am_embedded_god = false;
 static launch_t in_flight_msg_recv_client;
 static pthread_once_t _lc_once = PTHREAD_ONCE_INIT;
 
@@ -240,9 +241,12 @@
 	if ((lfd = _fd(socket(AF_UNIX, SOCK_STREAM, 0))) == -1) {
 		goto out_bad;
 	}
-		
+	
+#if TARGET_OS_EMBEDDED
+	(void)vproc_swap_integer(NULL, VPROC_GSK_EMBEDDEDROOTEQUIVALENT, NULL, &s_am_embedded_god);
+#endif
 	if (-1 == connect(lfd, (struct sockaddr *)&sun, sizeof(sun))) {
-		if( cifd != -1 ) {
+		if( cifd != -1 || s_am_embedded_god ) {
 			/* There is NO security enforced by this check. This is just a hint to our
 			 * library that we shouldn't error out due to failing to open this socket. If
 			 * we inherited a trusted file descriptor, we shouldn't fail. This should be
@@ -1018,7 +1022,7 @@
 	}
 
 	int fd2use = -1;
-	if( launch_data_get_type(d) == LAUNCH_DATA_STRING && strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) == 0 ) {
+	if( (launch_data_get_type(d) == LAUNCH_DATA_STRING && strcmp(launch_data_get_string(d), LAUNCH_KEY_CHECKIN) == 0) || s_am_embedded_god ) {
 		_lc->l->which = LAUNCHD_USE_CHECKIN_FD;
 	} else {
 		_lc->l->which = LAUNCHD_USE_OTHER_FD;

Modified: trunk/launchd/src/vproc_priv.h
===================================================================
--- trunk/launchd/src/vproc_priv.h	2009-04-02 22:41:10 UTC (rev 23879)
+++ trunk/launchd/src/vproc_priv.h	2009-04-03 01:35:19 UTC (rev 23880)
@@ -69,6 +69,7 @@
 	VPROC_GSK_PERUSER_RESUME,
 	VPROC_GSK_JOB_OVERRIDES_DB,
 	VPROC_GSK_JOB_CACHE_DB,
+	VPROC_GSK_EMBEDDEDROOTEQUIVALENT,
 } vproc_gsk_t;
 
 typedef unsigned int vproc_flags_t;
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.macosforge.org/pipermail/launchd-changes/attachments/20090402/bcd7cf72/attachment-0001.html>


More information about the launchd-changes mailing list