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