1/*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23
24#include <paths.h>
25#include <fcntl.h>
26#include "authhost.h"
27#include "server.h"
28#include <security_utilities/logging.h>
29#include <security_utilities/debugging.h>
30#include <security_agent_client/sa_request.h>
31#include <security_agent_client/utils.h>
32#include <bsm/audit.h>
33#include <bootstrap_priv.h>
34
35#include <grp.h>
36#include <pwd.h>
37#include <sys/types.h>
38#include <sys/sysctl.h>
39#include <syslog.h>
40#include <pthread.h>
41
42static pthread_once_t agent_cred_init = PTHREAD_ONCE_INIT;
43static gid_t agent_gid = 92;
44static uid_t agent_uid = 92;
45
46void initialize_agent_creds()
47{
48    struct passwd *agentUser = getpwnam("securityagent");
49    if (agentUser)
50    {
51        agent_uid = agentUser->pw_uid;
52        agent_gid = agentUser->pw_gid;
53        endpwent();
54    }
55}
56
57AuthHostInstance::AuthHostInstance(Session &session, AuthHostType host) :
58	mHostType(host)
59{
60	secdebug("authhost", "authhost born (%p)", this);
61	referent(session);
62	session.addReference(*this);
63    if (host == securityAgent)
64        pthread_once(&agent_cred_init, initialize_agent_creds);
65}
66
67AuthHostInstance::~AuthHostInstance()
68{
69	secdebug("authhost", "authhost died (%p)", this);
70}
71
72Session &AuthHostInstance::session() const
73{
74	return referent<Session>();
75}
76
77bool AuthHostInstance::inDarkWake()
78{
79	return session().server().inDarkWake();
80}
81
82void
83AuthHostInstance::childAction()
84{
85	// switch to desired session
86	CommonCriteria::AuditInfo &audit = this->session().auditInfo();
87	audit.get(audit.sessionId());
88	audit.set();
89	//this->session().auditInfo().set();
90
91	// Setup the environment for the SecurityAgent
92	unsetenv("USER");
93	unsetenv("LOGNAME");
94	unsetenv("HOME");
95
96	// close down any files that might have been open at this point
97	int maxDescriptors = getdtablesize ();
98	int i;
99
100	int devnull = open(_PATH_DEVNULL, O_RDWR, 0);
101	if (devnull >= 0) for (i = 0; i < 3; ++i)
102	{
103		dup2(devnull, i);
104	}
105
106	for (i = 3; i < maxDescriptors; ++i)
107	{
108		close (i);
109	}
110
111	// construct path to SecurityAgent
112	char agentExecutable[PATH_MAX + 1];
113	const char *path = getenv("SECURITYAGENT");
114	if (!path)
115		path = "/System/Library/CoreServices/SecurityAgent.app";
116	secdebug("adhoc", "hostType = %d", mHostType);
117
118	if ((mHostType == userAuthHost) || (mHostType == privilegedAuthHost))
119	{
120		snprintf(agentExecutable, sizeof(agentExecutable), "%s/Contents/Resources/authorizationhost", path);
121		secdebug("AuthHostInstance", "execl(%s)", agentExecutable);
122		execl(agentExecutable, agentExecutable, NULL);
123	}
124	else
125	{
126		snprintf(agentExecutable, sizeof(agentExecutable), "%s/Contents/MacOS/SecurityAgent", path);
127
128		pid_t pid = getpid();
129		if ((pid <= 0) ||
130            sysctlbyname("vfs.generic.noremotehang", NULL, NULL, &pid, sizeof(pid)))
131				syslog(LOG_ERR, "Failed to set vfs.generic.noremotehang for pid(%d)", pid);
132
133		setgroups(1, &agent_gid);
134		setgid(agent_gid);
135		setuid(agent_uid);
136
137		secdebug("AuthHostInstance", "execl(%s) as user (%d,%d)", agentExecutable, agent_uid, agent_gid);
138		execl(agentExecutable, agentExecutable, NULL);
139	}
140
141	secdebug("AuthHostInstance", "execl failed, errno=%d", errno);
142	// Unconditional suicide follows.
143	_exit(1);
144}
145
146// @@@  these definitions and the logic in lookup() should move into
147// libsecurity_agent
148#define SECURITYAGENT_BOOTSTRAP_NAME_BASE       "com.apple.SecurityAgent"
149#define AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE   "com.apple.authorizationhost"
150
151mach_port_t
152AuthHostInstance::lookup(SessionId jobId)
153{
154    StLock<Mutex> _(*this);
155
156    mach_port_t pluginhostPort = MACH_PORT_NULL;
157    kern_return_t result;
158    const char *serviceName;
159    /* PR-7483709 const */ uuid_t instanceId = UUID_INITIALIZER_FROM_SESSIONID(jobId);
160    uuid_string_t s;
161
162    if ((mHostType == securityAgent)) {
163	if (!(session().attributes() & sessionHasGraphicAccess))
164	    CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
165	if (inDarkWake())
166	    CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
167    }
168
169    if (mHostType == securityAgent)
170	serviceName = SECURITYAGENT_BOOTSTRAP_NAME_BASE;
171    else
172	serviceName = AUTHORIZATIONHOST_BOOTSTRAP_NAME_BASE;
173
174    secdebug("AuthHostInstance", "looking up %s instance %s", serviceName,
175      uuid_to_string(instanceId, s)); // XXX/gh  debugging
176    if ((result = bootstrap_look_up3(bootstrap_port, serviceName,
177      &pluginhostPort, 0, instanceId, BOOTSTRAP_SPECIFIC_INSTANCE)) != KERN_SUCCESS) {
178
179        Syslog::error("error %d looking up %s instance %s", result, serviceName,
180	  uuid_to_string(instanceId, s));
181    } else
182	secdebug("AuthHostInstance", "port = %x", (unsigned int)pluginhostPort);
183
184    return pluginhostPort;
185}
186
187Port AuthHostInstance::activate()
188{
189	StLock<Mutex> _(*this);
190	if (state() != alive)
191	{
192		if ((mHostType == securityAgent)) {
193		    if (!(session().attributes() & sessionHasGraphicAccess))
194			CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
195		    if (inDarkWake())
196			CssmError::throwMe(CSSM_ERRCODE_IN_DARK_WAKE);
197		}
198
199		fork();
200		switch (ServerChild::state()) {
201		case Child::alive:
202			secdebug("AuthHostInstance", "%p (pid %d) has launched", this, pid());
203			break;
204		case Child::dead:
205			secdebug("AuthHostInstance", "%p (pid %d) failed on startup", this, pid());
206			break;
207		default:
208			assert(false);
209		}
210	}
211
212	if (!ready())
213		CssmError::throwMe(CSSM_ERRCODE_NO_USER_INTERACTION);
214
215	return servicePort();
216}
217