1/*
2 * Copyright (c) 2000-2009,2012 Apple 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
25//
26// process - track a single client process and its belongings
27//
28#include "process.h"
29#include "server.h"
30#include "session.h"
31#include "tempdatabase.h"
32#include "authority.h"
33#include "child.h"          // ServerChild (really UnixPlusPlus::Child)::find()
34
35#include <security_utilities/logging.h>	//@@@ debug only
36#include "agentquery.h"
37
38
39//
40// Construct a Process object.
41//
42Process::Process(TaskPort taskPort,	const ClientSetupInfo *info, const CommonCriteria::AuditToken &audit)
43 :  mTaskPort(taskPort), mByteFlipped(false), mPid(audit.pid()), mUid(audit.euid()), mGid(audit.egid())
44{
45	StLock<Mutex> _(*this);
46
47	// set parent session
48	parent(Session::find(audit.sessionId(), true));
49
50    // let's take a look at our wannabe client...
51	if (mTaskPort.pid() != mPid) {
52		secdebug("SS", "Task/pid setup mismatch pid=%d task=%d(%d)",
53			mPid, mTaskPort.port(), mTaskPort.pid());
54		CssmError::throwMe(CSSMERR_CSSM_ADDIN_AUTHENTICATE_FAILED);	// you lied!
55	}
56
57	setup(info);
58	ClientIdentification::setup(this->pid());
59
60    // NB: ServerChild::find() should only be used to determine
61    // *existence*.  Don't use the returned Child object for anything else,
62    // as it is not protected against its underlying process's destruction.
63	if (this->pid() == getpid() // called ourselves (through some API). Do NOT record this as a "dirty" transaction
64        || ServerChild::find<ServerChild>(this->pid()))   // securityd's child; do not mark this txn dirty
65		VProc::Transaction::deactivate();
66
67	if (SECURITYD_CLIENT_NEW_ENABLED())
68		SECURITYD_CLIENT_NEW(this, this->pid(), &this->session(),
69			(char *)codePath(this->processCode()).c_str(), taskPort, mUid, mGid, mByteFlipped);
70}
71
72
73//
74// Screen a process setup request for an existing process.
75// This means the client has requested intialization even though we remember having
76// talked to it in the past. This could either be an exec(2), or the client could just
77// have forgotten all about its securityd client state. Or it could be an attack...
78//
79void Process::reset(TaskPort taskPort, const ClientSetupInfo *info, const CommonCriteria::AuditToken &audit)
80{
81	StLock<Mutex> _(*this);
82	if (taskPort != mTaskPort) {
83		secdebug("SS", "Process %p(%d) reset mismatch (tp %d-%d)",
84			this, pid(), taskPort.port(), mTaskPort.port());
85		//@@@ CssmError::throwMe(CSSM_ERRCODE_VERIFICATION_FAILURE);		// liar
86	}
87	setup(info);
88	CFCopyRef<SecCodeRef> oldCode = processCode();
89
90	ClientIdentification::setup(this->pid());	// re-constructs processCode()
91	if (CFEqual(oldCode, processCode())) {
92		SECURITYD_CLIENT_RESET_AMNESIA(this);
93	} else {
94		SECURITYD_CLIENT_RESET_FULL(this);
95		CodeSigningHost::reset();
96	}
97}
98
99
100//
101// Common set processing
102//
103void Process::setup(const ClientSetupInfo *info)
104{
105	// process setup info
106	assert(info);
107	uint32 pversion;
108	if (info->order == 0x1234) {	// right side up
109		pversion = info->version;
110		mByteFlipped = false;
111	} else if (info->order == 0x34120000) { // flip side up
112		pversion = flip(info->version);
113		mByteFlipped = true;
114	} else // non comprende
115		CssmError::throwMe(CSSM_ERRCODE_INCOMPATIBLE_VERSION);
116
117	// check wire protocol version
118	if (pversion != SSPROTOVERSION)
119		CssmError::throwMe(CSSM_ERRCODE_INCOMPATIBLE_VERSION);
120}
121
122
123//
124// Clean up a Process object
125//
126Process::~Process()
127{
128	SECURITYD_CLIENT_RELEASE(this, this->pid());
129
130	// tell all our authorizations that we're gone
131	IFDEBUG(if (!mAuthorizations.empty())
132		secdebug("SS", "Process %p(%d) clearing %d authorizations",
133			this, mPid, int(mAuthorizations.size())));
134	for (AuthorizationSet::iterator it = mAuthorizations.begin();
135			it != mAuthorizations.end(); ) {
136        AuthorizationToken *auth = *it;
137        while (++it != mAuthorizations.end() && *it == auth) ;	// Skip duplicates
138		if (auth->endProcess(*this))
139			delete auth;
140    }
141
142    // release our name for the process's task port
143	if (mTaskPort)
144        mTaskPort.destroy();
145}
146
147void Process::kill()
148{
149	StLock<Mutex> _(*this);
150
151	// release local temp store
152	mLocalStore = NULL;
153
154	// standard kill processing
155	PerProcess::kill();
156}
157
158
159Session& Process::session() const
160{
161	return parent<Session>();
162}
163
164
165void Process::checkSession(const audit_token_t &auditToken)
166{
167	AuditToken audit(auditToken);
168	if (audit.sessionId() != this->session().sessionId())
169		this->changeSession(audit.sessionId());
170}
171
172
173LocalDatabase &Process::localStore()
174{
175	StLock<Mutex> _(*this);
176	if (!mLocalStore)
177		mLocalStore = new TempDatabase(*this);
178	return *mLocalStore;
179}
180
181Key *Process::makeTemporaryKey(const CssmKey &key, CSSM_KEYATTR_FLAGS moreAttributes,
182	const AclEntryPrototype *owner)
183{
184	return safer_cast<TempDatabase&>(localStore()).makeKey(key, moreAttributes, owner);
185}
186
187
188//
189// Change the session of a process.
190// This is the result of SessionCreate from a known process client.
191//
192void Process::changeSession(Session::SessionId sessionId)
193{
194	// re-parent
195	parent(Session::find(sessionId, true));
196	SECURITYD_CLIENT_CHANGE_SESSION(this, &this->session());
197}
198
199
200//
201// Authorization set maintainance
202//
203void Process::addAuthorization(AuthorizationToken *auth)
204{
205	assert(auth);
206	StLock<Mutex> _(*this);
207	mAuthorizations.insert(auth);
208	auth->addProcess(*this);
209}
210
211void Process::checkAuthorization(AuthorizationToken *auth)
212{
213	assert(auth);
214	StLock<Mutex> _(*this);
215	if (mAuthorizations.find(auth) == mAuthorizations.end())
216		MacOSError::throwMe(errAuthorizationInvalidRef);
217}
218
219bool Process::removeAuthorization(AuthorizationToken *auth)
220{
221	assert(auth);
222	StLock<Mutex> _(*this);
223	// we do everything with a single set lookup call...
224	typedef AuthorizationSet::iterator Iter;
225	Iter it = mAuthorizations.lower_bound(auth);
226	bool isLast;
227	if (it == mAuthorizations.end() || auth != *it) {
228		isLast = true;
229	} else {
230		Iter next = it; ++next;			// following element
231		isLast = (next == mAuthorizations.end()) || auth != *next;
232		mAuthorizations.erase(it);		// remove first match
233	}
234	if (isLast) {
235		if (auth->endProcess(*this))	// ... tell it to remove us,
236			return true;				// ... and tell the caller
237	}
238	return false;						// keep the auth; it's still in use
239}
240
241
242//
243// Debug dump support
244//
245#if defined(DEBUGDUMP)
246
247void Process::dumpNode()
248{
249	PerProcess::dumpNode();
250	if (mByteFlipped)
251		Debug::dump(" FLIPPED");
252	Debug::dump(" task=%d pid=%d uid/gid=%d/%d",
253		mTaskPort.port(), mPid, mUid, mGid);
254	CodeSigningHost::dump();
255	ClientIdentification::dump();
256}
257
258#endif //DEBUGDUMP
259