]> git.saurik.com Git - apple/securityd.git/blob - src/transwalkers.h
e9476a7992f333a07e70100d05ecd7b9027480f7
[apple/securityd.git] / src / transwalkers.h
1 /*
2 * Copyright (c) 2003-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
25 //
26 // transwalkers - server side transition data walking support
27 //
28 // These are data walker operators for securely marshaling and unmarshaling
29 // data structures across IPC. They are also in charge of fixing byte order
30 // inconsistencies between server and clients.
31 //
32 #ifndef _H_TRANSWALKERS
33 #define _H_TRANSWALKERS
34
35 #include <security_cdsa_utilities/AuthorizationWalkers.h>
36 #include "flippers.h"
37 #include "server.h"
38 #include <security_cdsa_utilities/context.h>
39
40 using LowLevelMemoryUtilities::increment;
41 using LowLevelMemoryUtilities::difference;
42
43
44 //
45 // Should we flip data?
46 // This looks at the current client's process information (a thread-global state)
47 // to determine flip status. Valid (only) within BEGIN_IPC/END_IPC brackets.
48 //
49 bool flipClient();
50
51
52 //
53 // A CheckingReconstituteWalker is a variant of an ordinary ReconstituteWalker
54 // that checks object pointers and sizes against the incoming block limits.
55 // It throws an exception if incoming data has pointers outside the incoming block.
56 // This avoids trouble inside of securityd caused (by bug or malice)
57 // from someone spoofing the client access side.
58 //
59 class CheckingReconstituteWalker {
60 private:
61 void check(void *addr, size_t size)
62 {
63 if (addr < mBase || increment(addr, size) > mLimit)
64 CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
65 }
66
67 public:
68 CheckingReconstituteWalker(void *ptr, void *base, size_t size, bool flip);
69
70 template <class T>
71 void operator () (T &obj, size_t size = sizeof(T))
72 {
73 check(increment(&obj, -mOffset), size);
74 if (mFlip)
75 Flippers::flip(obj);
76 }
77
78 template <class T>
79 void operator () (T * &addr, size_t size = sizeof(T))
80 {
81 DEBUGWALK("checkreconst:ptr");
82 if (addr) {
83 // process the pointer
84 void *p = addr;
85 blob(p, size);
86 addr = reinterpret_cast<T *>(p);
87
88 // now flip the contents
89 if (mFlip)
90 Flippers::flip(*addr);
91 }
92 }
93
94 template <class T>
95 void blob(T * &addr, size_t size)
96 {
97 DEBUGWALK("checkreconst:blob");
98 if (addr) {
99 // flip the address (the pointer itself)
100 if (mFlip) {
101 secdebug("flippers", "flipping %s@%p", Debug::typeName(addr).c_str(), addr);
102 Flippers::flip(addr);
103 }
104
105 // check the address against the transmitted bounds
106 check(addr, size);
107
108 // relocate it
109 addr = increment<T>(addr, mOffset);
110 }
111 }
112
113 static const bool needsRelinking = true;
114 static const bool needsSize = false;
115
116 private:
117 void *mBase; // old base address
118 void *mLimit; // old last byte address + 1
119 off_t mOffset; // relocation offset
120 bool mFlip; // apply byte order flipping
121 };
122
123
124 //
125 // Fix DBAttributes, which have to be processed specially
126 //
127 void fixDbAttributes (CssmDbAttributeData &data);
128 void fixDbAttributes (CssmQuery &query);
129 void fixDbAttributes (CssmDbRecordAttributeData &data);
130
131 template<class T>
132 void fixDbAttributes(T &n) {} // handle the default case
133
134
135 //
136 // Process an incoming (IPC) data blob of type T.
137 // This relocates pointers to fit in the local address space,
138 // and fixes byte order issues as needed.
139 //
140 template <class T>
141 void relocate(T *obj, T *base, size_t size)
142 {
143 if (obj) {
144 if (base == NULL) // invalid, could confuse walkers
145 CssmError::throwMe(CSSM_ERRCODE_INVALID_POINTER);
146 CheckingReconstituteWalker relocator(obj, base, size,
147 Server::process().byteFlipped());
148 walk(relocator, base);
149
150 // resolve weird type interdependency in DB_ATTRIBUTE_DATA
151 if (Server::process().byteFlipped())
152 fixDbAttributes(*obj);
153 }
154 }
155
156
157 //
158 // Special handling for incoming CSSM contexts.
159 //
160 void relocate(Context &context, void *base, Context::Attr *attrs, uint32 attrSize);
161
162
163 //
164 // A FlipWalker is a walker operator that collects its direct invocations
165 // into a set of memory objects. These objects can then collectively be
166 // byte-flipped (exactly once :-) at the flick of a method.
167 //
168 class FlipWalker {
169 private:
170 struct Base {
171 virtual ~Base() { }
172 virtual void flip() const = 0;
173 };
174
175 template <class T>
176 struct FlipRef : public Base {
177 T &obj;
178 FlipRef(T &s) : obj(s) { }
179 void flip() const { Flippers::flip(obj); }
180 };
181
182 template <class T>
183 struct FlipPtr : public Base {
184 T * &obj;
185 FlipPtr(T * &s) : obj(s) { }
186 void flip() const { Flippers::flip(*obj); Flippers::flip(obj); }
187 };
188
189 template <class T>
190 struct FlipBlob : public Base {
191 T * &obj;
192 FlipBlob(T * &s) : obj(s) { }
193 void flip() const { Flippers::flip(obj); }
194 };
195
196 struct Flipper {
197 Base *impl;
198 Flipper(Base *p) : impl(p) { }
199 bool operator < (const Flipper &other) const
200 { return impl < other.impl; }
201 };
202
203 public:
204 ~FlipWalker();
205 void doFlips(bool active = true);
206
207 template <class T>
208 void operator () (T &obj, size_t = sizeof(T))
209 { mFlips.insert(new FlipRef<T>(obj)); }
210
211 template <class T>
212 T *operator () (T * &addr, size_t size = sizeof(T))
213 { mFlips.insert(new FlipPtr<T>(addr)); return addr; }
214
215 template <class T>
216 void blob(T * &addr, size_t size)
217 { mFlips.insert(new FlipBlob<T>(addr)); }
218
219 static const bool needsRelinking = true;
220 static const bool needsSize = true;
221
222 private:
223 set<Flipper> mFlips;
224 };
225
226
227 //
228 // A raw flip, conditioned on the client's flip state
229 //
230 template <class T>
231 void flip(T &addr)
232 {
233 if (flipClient()) {
234 secdebug("flippers", "raw flipping %s", Debug::typeName(addr).c_str());
235 Flippers::flip(addr);
236 }
237 }
238
239
240 void flipCssmDbAttributeData (CssmDbRecordAttributeData *value, CssmDbRecordAttributeData **&addr, CssmDbRecordAttributeData **&base);
241
242 //
243 // Take an object at value, flip it, and return appropriately flipped
244 // addr/base pointers ready to be returned through IPC.
245 // Note that this doesn't set the outgoing length (aka 'fooLength') field.
246 //
247 template <class T>
248 void flips(T *value, T ** &addr, T ** &base)
249 {
250 *addr = *base = value;
251 if (flipClient()) {
252 // resolve weird type inter-dependency in DB_ATTRIBUTE_DATA
253 if (value)
254 fixDbAttributes(*value);
255 FlipWalker w; // collector
256 walk(w, value); // collect all flippings needed
257 w.doFlips(); // execute flips (flips value but leaves addr alone)
258 Flippers::flip(*base); // flip base (so it arrives right side up)
259 }
260 }
261
262 //
263 // Take a DATA type RPC argument purportedly representing a Blob of some kind,
264 // turn it into a Blob, and fail properly if it's not kosher.
265 //
266 template <class BlobType>
267 const BlobType *makeBlob(const CssmData &blobData, CSSM_RETURN error = CSSM_ERRCODE_INVALID_DATA)
268 {
269 if (!blobData.data() || blobData.length() < sizeof(BlobType))
270 CssmError::throwMe(error);
271 const BlobType *blob = static_cast<const BlobType *>(blobData.data());
272 if (blob->totalLength != blobData.length())
273 CssmError::throwMe(error);
274 return blob;
275 }
276
277
278 //
279 // An OutputData object will take memory allocated within securityd,
280 // hand it to the MIG return-output parameters, and schedule it to be released
281 // after the MIG reply has been sent. It will also get rid of it in case of
282 // error.
283 //
284 class OutputData : public CssmData {
285 public:
286 OutputData(void **outP, mach_msg_type_number_t *outLength)
287 : mData(*outP), mLength(*outLength) { }
288 ~OutputData()
289 { mData = data(); mLength = length(); Server::releaseWhenDone(mData); }
290
291 void operator = (const CssmData &source)
292 { CssmData::operator = (source); }
293
294 private:
295 void * &mData;
296 mach_msg_type_number_t &mLength;
297 };
298
299
300 //
301 // Choose a Database from a choice of two sources, giving preference
302 // to persistent stores and to earlier sources.
303 //
304 Database *pickDb(Database *db1, Database *db2);
305
306 static inline Database *dbOf(Key *key) { return key ? &key->database() : NULL; }
307
308 inline Database *pickDb(Key *k1, Key *k2) { return pickDb(dbOf(k1), dbOf(k2)); }
309 inline Database *pickDb(Database *db1, Key *k2) { return pickDb(db1, dbOf(k2)); }
310 inline Database *pickDb(Key *k1, Database *db2) { return pickDb(dbOf(k1), db2); }
311
312
313 #endif //_H_TRANSWALKERS