How does SELinux enforce policy?
First let's talk about the boot process
When the SELinux kernel boots up it is hard coded to run as kernel_t. Since at this point there is no policy running, this is the only context. So since this is the only context, all applications that are run will stay in kernel_t. When the kernel executes /sbin/init it is originally running as kernel_t, then it reads in the policy file and loads it into the kernel. At this point init re-execs itself. When /sbin/init is installed it is labeled init_exec_t and now there is a rule in the kernel that says when kernel_t execs an application labeled init_exec_t, it should transition to init_t. So now the init process is running as init_t.
The init process then runs /etc/rc.d/rc.sysinit, which is labeled initrc_exec_t. The kernel has a rule that says when init_t execs initrc_exec_t it transitions to initrc_t. So this continues until the httpd executable gets started as httpd_t.
The kernel will now examine every access (read) of the process domain (httpd_t) on the class (file) of objects on the system, e.g. /var/www/index.html (httpd_sys_content_t). If there is a rule in policy that allows it, the access will go forward. If it is denied, an AVC, Access Vector Cache, message will be generated in the log file. By the way don't blame me for AVC, I was not the one who chose the name.
What about unconfined_t and the login process?
Certain applications, such as login, are allowed by policy to set the context of the next executed program. Actually most of the login programs are using pam_selinux to get this behavior.
So when I log in to the system. pam uses libselinux to figure out what the initial context setup for username dwalsh is. On a targeted system this will be unconfined_t. You can modify this behavior by changing files in /etc/selinux/POLICYTYPE/contexts/default_contexts and /etc/selinux/POLICYTYPE/contexts/users/ so that on a strict or MLS policy machine, you could setup dwalsh logging in a local terminal to default to one context, and on a remote login to use a different context, but that is an advanced feature.
So once the user is logged in, his shell will be running with unconfined_t or user_t, and every access that the user makes is going through the kernel to make sure the access is allowed. Obviously this is all cached to make the overhead as small as possible.
One curious thing that you might have noticed, is that depending on how you start an application, it could end up running in a different context. For example, if I run httpd via the service start up script it will run as httpd_t, but if I run it directly, it will run as unconfined_t. Why? There is a rule in the kernel that says unconfined_t running initrc_exec_t will transition to initrc_t, and initrc_t running httpd_exec_t will transition to httpd_t. There is also a rule that says unconfined_t is allowed to run httpd_exec_t without transitioning.
This was a conscious decision made by the policy writers to attempt to lessen confusion. You see, most confined domains are not allowed to talk to the terminal. They are also not allowed to read/write users' home directories. The reason for this is that we are trying to protect user space from system space, and if a hacked targeted domain was able to read/write the console or terminal, it could put up a login screen and us lemmings would just enter our username and password. Similarly, if a targeted domain was able to read/write our homedirs it could read our credit card data, modify our login scripts, or generally cause havoc.
So one problem with this is that administrators sometimes run the targeted domain with --help or want to test a CGI script and they would see no output. Or would attempt to redirect the output to their home directories.
./mycgi_script >> ~dwalsh.out
If these domains transitioned, these accesses would be denied, and the user would have a hard time understanding what happened.
So if you want to run a targeted domain under the locked down context, you need to use the init scripts.