1/*
2 * Copyright (c) 2000-2004,2006-2007,2011,2013 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// acl_preauth - a subject type for modeling PINs and similar slot-specific
27//		pre-authentication schemes.
28//
29#include "acl_preauth.h"
30#include <security_utilities/debugging.h>
31
32
33namespace Security {
34namespace PreAuthorizationAcls {
35
36
37//
38// Origin forms
39//
40AclSubject *OriginMaker::make(const TypedList &list) const
41{
42	ListElement *args[1];
43	crack(list, 1, args, CSSM_LIST_ELEMENT_WORDID);
44		return new OriginAclSubject(*args[0]);
45}
46
47AclSubject *OriginMaker::make(AclSubject::Version version, Reader &pub, Reader &) const
48{
49	// just an integer containing the auth tag
50	Endian<uint32> auth;
51	pub(auth);
52	return new OriginAclSubject(AclAuthorization(auth));
53}
54
55
56//
57// Validate the origin form.
58// This tries to find the source AclObject and hands the question off to it.
59// If anything isn't right, fail the validation.
60//
61bool OriginAclSubject::validate(const AclValidationContext &ctx) const
62{
63	if (Environment *env = ctx.environment<Environment>())
64		if (ObjectAcl *source = env->preAuthSource())
65			if (source->validates(mAuthTag, ctx.cred(), ctx.environment()))
66				return true;
67
68	// no joy (the sad default)
69	return false;
70}
71
72
73CssmList OriginAclSubject::toList(Allocator &alloc) const
74{
75	return TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PREAUTH,
76		new(alloc) ListElement(mAuthTag));
77}
78
79OriginAclSubject::OriginAclSubject(AclAuthorization auth)
80	: AclSubject(CSSM_ACL_SUBJECT_TYPE_PREAUTH), mAuthTag(auth)
81{
82	if (auth < CSSM_ACL_AUTHORIZATION_PREAUTH_BASE || auth >= CSSM_ACL_AUTHORIZATION_PREAUTH_END)
83		CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
84}
85
86
87//
88// Origin exported form is just a four-byte integer (preauth authorization tag)
89//
90void OriginAclSubject::exportBlob(Writer::Counter &pub, Writer::Counter &priv)
91{
92	Endian<uint32> auth = mAuthTag;
93	pub(auth);
94}
95
96void OriginAclSubject::exportBlob(Writer &pub, Writer &priv)
97{
98	Endian<uint32> auth = mAuthTag;
99	pub(auth);
100}
101
102
103//
104// Now for the other side of the coin.
105// SourceAclSubjects describe the unusual side (for ACL management) of this game.
106// The AclSubject of a preauth source MUST be of PREAUTH_SOURCE type. This subject
107// contains the actual validation conditions as a sub-subject, and may provide
108// additional information to represent known state of the preauth system.
109//
110// Think of the extra data in a PreAuthSource ACL as "current state informational"
111// that only exists internally, and in the CssmList view. It does not get put into
112// persistent (externalized) ACL storage at all. (After all, there's nothing persistent
113// about it.)
114//
115AclSubject *SourceMaker::make(const TypedList &list) const
116{
117	// minimum requirement: item[1] = sub-subject (sublist)
118	if (list.length() < 2)
119        CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
120	ListElement &sub = list[1];
121	RefPointer<AclSubject> subSubject = ObjectAcl::make(sub);
122
123	// anything else is interpreted as tracking state (defaulted if missing)
124	switch (list.length()) {
125	case 2:		// no tracking state
126		return new SourceAclSubject(subSubject);
127	case 3:
128		if (list[2].type() == CSSM_LIST_ELEMENT_WORDID)
129			return new SourceAclSubject(subSubject, list[2]);
130		// fall through
131	default:
132		CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
133	}
134}
135
136AclSubject *SourceMaker::make(AclSubject::Version version, Reader &pub, Reader &priv) const
137{
138	// external form does not contain tracking state - defaults to unknown
139	RefPointer<AclSubject> subSubject = ObjectAcl::importSubject(pub, priv);
140	return new SourceAclSubject(subSubject);
141}
142
143
144//
145// Source validation uses its own home-cooked validation context.
146//
147class SourceValidationContext : public AclValidationContext {
148public:
149	SourceValidationContext(const AclValidationContext &base)
150		: AclValidationContext(base), mCredTag(base.entryTag()) { }
151
152	uint32 count() const	{ return cred() ? cred()->samples().length() : 0; }
153	uint32 size() const		{ return count(); }
154	const TypedList &sample(uint32 n) const
155	{ assert(n < count()); return cred()->samples()[n]; }
156
157	const char *credTag() const { return mCredTag; }	// override
158
159	void matched(const TypedList *) const { }	//@@@ prelim
160
161private:
162	const char *mCredTag;
163};
164
165bool SourceAclSubject::SourceAclSubject::validate(const AclValidationContext &baseCtx) const
166{
167	// try to authenticate our sub-subject
168	if (Environment *env = baseCtx.environment<Environment>()) {
169		AclAuthorization auth = baseCtx.authorization();
170		if (!CSSM_ACL_AUTHORIZATION_IS_PREAUTH(auth))	// all muddled up; bail
171			CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
172		uint32 slot = CSSM_ACL_AUTHORIZATION_PREAUTH_SLOT(auth);
173		secdebug("preauth", "using state %d@%p", slot, &env->store(this));
174		bool &accepted = env->store(this).attachment<AclState>((void *)((size_t) slot)).accepted;
175		if (!accepted) {
176			secdebug("preauth", "%p needs to authenticate its subject", this);
177			SourceValidationContext ctx(baseCtx);
178			if (mSourceSubject->validate(ctx)) {
179				secdebug("preauth", "%p pre-authenticated", this);
180				accepted = true;
181			}
182		}
183		return accepted;
184	}
185	return false;
186}
187
188
189CssmList SourceAclSubject::toList(Allocator &alloc) const
190{
191	return TypedList(alloc, CSSM_ACL_SUBJECT_TYPE_PREAUTH_SOURCE,
192		new(alloc) ListElement(mSourceSubject->toList(alloc)));
193}
194
195
196SourceAclSubject::SourceAclSubject(AclSubject *subSubject, CSSM_ACL_PREAUTH_TRACKING_STATE state)
197	: AclSubject(CSSM_ACL_SUBJECT_TYPE_PREAUTH),
198	  mSourceSubject(subSubject)
199{
200}
201
202
203//
204// Export the subject to a memory blob
205//
206void SourceAclSubject::exportBlob(Writer::Counter &pub, Writer::Counter &priv)
207{
208	mSourceSubject->exportBlob(pub, priv);
209}
210
211void SourceAclSubject::exportBlob(Writer &pub, Writer &priv)
212{
213	mSourceSubject->exportBlob(pub, priv);
214	// tracking state is not exported
215}
216
217
218#ifdef DEBUGDUMP
219
220void OriginAclSubject::debugDump() const
221{
222	Debug::dump("Preauth(to slot %d)", mAuthTag - CSSM_ACL_AUTHORIZATION_PREAUTH_BASE);
223}
224
225void SourceAclSubject::debugDump() const
226{
227	Debug::dump("Preauth source: ");
228	if (mSourceSubject)
229		mSourceSubject->debugDump();
230	else
231		Debug::dump("NULL?");
232}
233
234#endif //DEBUGDUMP
235
236
237}	//  namespace PreAuthorizationAcls
238}	//  namespace Security
239