]> git.saurik.com Git - apple/securityd.git/blob - src/acl_keychain.cpp
29702174c6435dddb0f7c4da3564742cbb101ac9
[apple/securityd.git] / src / acl_keychain.cpp
1 /*
2 * Copyright (c) 2000-2004 Apple Computer, Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * Copyright (c) 1999-2003 Apple Computer, Inc. All Rights Reserved.
7 *
8 * This file contains Original Code and/or Modifications of Original Code
9 * as defined in and that are subject to the Apple Public Source License
10 * Version 2.0 (the 'License'). You may not use this file except in
11 * compliance with the License. Please obtain a copy of the License at
12 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * file.
14 *
15 * The Original Code and all software distributed under the License are
16 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
17 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
18 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
20 * Please see the License for the specific language governing rights and
21 * limitations under the License.
22 *
23 * @APPLE_LICENSE_HEADER_END@
24 */
25
26
27 //
28 // acl_keychain - a subject type for the protected-path
29 // keychain prompt interaction model.
30 //
31 // Arguments in CSSM_LIST form:
32 // list[1] = CssmData: CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR structure
33 // list[2] = CssmData: Descriptive String (presented to user in protected dialogs)
34 // For legacy compatibility, we accept a single-entry form
35 // list[1] = CssmData: Descriptive String
36 // which defaults to a particular CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR structure value.
37 // This is never produced by current code, and is considered purely a legacy feature.
38 //
39 // On-disk (flattened) representation:
40 // In order to accommodate legacy formats nicely, we use the binary-versioning feature
41 // of the ACL machinery. Version 0 is the legacy format (storing only the description
42 // string), while Version 1 contains both selector and description. To allow for
43 // maximum backward compatibility, legacy-compatible forms are written out as version 0.
44 // See isLegacyCompatible().
45 //
46 // Some notes on Acl Update Triggers:
47 // When the user checks the "don't ask me again" checkbox in the access confirmation
48 // dialog, we respond by returning the informational error code
49 // CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT, and setting a count-down trigger
50 // in the connection. The caller is entitled to bypass our dialog (it succeeds
51 // automatically) within the next few (Connection::aclUpdateTriggerLimit == 3)
52 // requests, in order to update the object's ACL as requested. It must then retry
53 // the original access operation (which will presumably pass because of that edit).
54 // These are the rules: for the trigger to apply, the access must be to the same
55 // object, from the same connection, and within the next aclUpdateTriggerLimit accesses.
56 // (Currently, these are for a "get acl", "get owner", and the "change acl" calls.)
57 // Damage Control Department: The worst this mechanism could do, if subverted, is
58 // to bypass our confirmation dialog (making it appear to succeed to the ACL validation).
59 // But that is exactly what the "don't ask me again" checkbox is meant to do, so any
60 // subversion would be based on a (perhaps intentional) miscommunication between user
61 // and client process as to what the user consents not to be asked about (any more).
62 // The user can always examine the resulting ACL (in Keychain Access or elsewhere), and
63 // edit it to suit her needs.
64 //
65 #include "acl_keychain.h"
66 #include "agentquery.h"
67 #include "acls.h"
68 #include "connection.h"
69 #include "database.h"
70 #include "server.h"
71 #include <security_utilities/debugging.h>
72 #include <algorithm>
73
74
75 #define ACCEPT_LEGACY_FORM 1
76 #define FECKLESS_KEYCHAIN_ACCESS_EXCEPTION 1
77
78
79 //
80 // The default for the selector structure.
81 //
82 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR KeychainPromptAclSubject::defaultSelector = {
83 CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION, // version
84 0 // flags
85 };
86
87
88 //
89 // Validate a credential set against this subject.
90 //
91 bool KeychainPromptAclSubject::validate(const AclValidationContext &context,
92 const TypedList &sample) const
93 {
94 if (SecurityServerEnvironment *env = context.environment<SecurityServerEnvironment>()) {
95 // check for special ACL-update override
96 if (context.authorization() == CSSM_ACL_AUTHORIZATION_CHANGE_ACL
97 && Server::connection().aclWasSetForUpdateTrigger(env->acl)) {
98 secdebug("kcacl", "honoring acl update trigger for %p(%s)",
99 &env->acl, description.c_str());
100 return true;
101 }
102
103 // does the user need to type in the passphrase?
104 const Database *db = env->database();
105 bool needPassphrase = db && (selector.flags & CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE);
106
107 // ask the user
108 #if FECKLESS_KEYCHAIN_ACCESS_EXCEPTION
109 Process &process = Server::process();
110 secdebug("kcacl", "Keychain query from process %d (UID %d)", process.pid(), process.uid());
111 if (process.clientCode())
112 needPassphrase |=
113 process.clientCode()->canonicalPath() == "/Applications/Utilities/Keychain Access.app";
114 #endif
115 QueryKeychainUse query(needPassphrase, db);
116 query.inferHints(Server::process());
117
118 if (query.queryUser(db ? db->dbName() : NULL,
119 description.c_str(), context.authorization())
120 != SecurityAgent::noReason)
121 return false;
122
123 // process "always allow..." response
124 if (query.remember) {
125 // mark for special ACL-update override (really soon) later
126 Server::connection().setAclUpdateTrigger(env->acl);
127 secdebug("kcacl", "setting acl update trigger for %p(%s)",
128 &env->acl, description.c_str());
129 // fail with prejudice (caller will retry)
130 CssmError::throwMe(CSSMERR_CSP_APPLE_ADD_APPLICATION_ACL_SUBJECT);
131 }
132
133 // finally, return the actual user response
134 return query.allow;
135 }
136 return false; // default to deny without prejudice
137 }
138
139
140 //
141 // Make a copy of this subject in CSSM_LIST form
142 //
143 CssmList KeychainPromptAclSubject::toList(Allocator &alloc) const
144 {
145 // always issue new (non-legacy) form
146 return TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT,
147 new(alloc) ListElement(alloc, CssmData::wrap(selector)),
148 new(alloc) ListElement(alloc, description));
149 }
150
151
152 //
153 // Create a KeychainPromptAclSubject
154 //
155 KeychainPromptAclSubject *KeychainPromptAclSubject::Maker::make(const TypedList &list) const
156 {
157 switch (list.length()) {
158 #if ACCEPT_LEGACY_FORM
159 case 2: // legacy case: just description
160 {
161 ListElement *params[1];
162 crack(list, 1, params, CSSM_LIST_ELEMENT_DATUM);
163 return new KeychainPromptAclSubject(*params[0], defaultSelector);
164 }
165 #endif //ACCEPT_LEGACY_FORM
166 case 3: // standard case: selector + description
167 {
168 ListElement *params[2];
169 crack(list, 2, params, CSSM_LIST_ELEMENT_DATUM, CSSM_LIST_ELEMENT_DATUM);
170 return new KeychainPromptAclSubject(*params[1],
171 *params[0]->data().interpretedAs<CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR>(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE));
172 }
173 default:
174 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
175 }
176 }
177
178 KeychainPromptAclSubject *KeychainPromptAclSubject::Maker::make(Version version,
179 Reader &pub, Reader &) const
180 {
181 CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR selector;
182 const char *description;
183 switch (version) {
184 case pumaVersion:
185 selector = defaultSelector;
186 pub(description);
187 break;
188 case jaguarVersion:
189 pub(selector);
190 selector.version = n2h (selector.version);
191 selector.flags = n2h (selector.flags);
192 pub(description);
193 break;
194 }
195 return new KeychainPromptAclSubject(description, selector);
196 }
197
198 KeychainPromptAclSubject::KeychainPromptAclSubject(string descr,
199 const CSSM_ACL_KEYCHAIN_PROMPT_SELECTOR &sel)
200 : SimpleAclSubject(CSSM_ACL_SUBJECT_TYPE_KEYCHAIN_PROMPT, CSSM_SAMPLE_TYPE_KEYCHAIN_PROMPT),
201 selector(sel), description(descr)
202 {
203 // check selector version
204 if (selector.version != CSSM_ACL_KEYCHAIN_PROMPT_CURRENT_VERSION)
205 CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
206
207 // determine binary compatibility version
208 if (selector.flags == 0) // compatible with old form
209 version(pumaVersion);
210 else
211 version(jaguarVersion);
212 }
213
214
215 //
216 // Export the subject to a memory blob
217 //
218 void KeychainPromptAclSubject::exportBlob(Writer::Counter &pub, Writer::Counter &priv)
219 {
220 if (version() != 0) {
221 selector.version = h2n (selector.version);
222 selector.flags = h2n (selector.flags);
223 pub(selector);
224 }
225
226 pub.insert(description.size() + 1);
227 }
228
229 void KeychainPromptAclSubject::exportBlob(Writer &pub, Writer &priv)
230 {
231 if (version() != 0) {
232 selector.version = h2n (selector.version);
233 selector.flags = h2n (selector.flags);
234 pub(selector);
235 }
236 pub(description.c_str());
237 }
238
239
240 //
241 // Determine whether this ACL subject is in "legacy compatible" form.
242 // Legacy (<10.2) form contained no selector.
243 //
244 bool KeychainPromptAclSubject::isLegacyCompatible() const
245 {
246 return selector.flags == 0;
247 }
248
249
250 #ifdef DEBUGDUMP
251
252 void KeychainPromptAclSubject::debugDump() const
253 {
254 Debug::dump("KeychainPrompt:%s(%s)",
255 description.c_str(),
256 (selector.flags & CSSM_ACL_KEYCHAIN_PROMPT_REQUIRE_PASSPHRASE) ? "passphrase" : "standard");
257 }
258
259 #endif //DEBUGDUMP