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