1/*
2 * Copyright (c) 2000-2004,2006,2011,2014 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_threshold - Threshold-based group ACL subjects
27//
28#include <security_cdsa_utilities/acl_threshold.h>
29#include <algorithm>
30#include <security_utilities/endian.h>
31
32
33//
34// Validate a credential set against this subject.
35//
36// With STRICTCOUNTING set, we assume that every match in the threshold ACL
37// "consumes" one sample in the corresponding threshold sample. This will not
38// work as expected for subject types that may succeed without a sample (e.g. ANY)
39// or subject types that may multiply match against a single sample. You have been
40// warned.
41//
42class SublistValidationContext : public AclValidationContext {
43public:
44    SublistValidationContext(const AclValidationContext &ctx, const TypedList &list)
45    : AclValidationContext(ctx), sampleList(list) { }
46
47    uint32 count() const { return sampleList.length() - 1; }
48    const TypedList &sample(uint32 n) const
49    { return TypedList::overlay(sampleList[n+1].list()); }
50
51	void matched(const TypedList *) const { }	//@@@ ignore sub-matches for now
52
53    const TypedList &sampleList;
54};
55
56bool ThresholdAclSubject::validate(const AclValidationContext &baseCtx,
57    const TypedList &sample) const
58{
59#ifdef STRICTCOUNTING
60    // Pre-screen for reasonable number of subsamples.
61    // We could more strictly require subSampleCount == elements.length();
62    // this is more flexible in that it allows the caller to abbreviate.
63    uint32 subSampleCount = sample.length() - 1;	// (drop type header)
64    if (subSampleCount < minimumNeeded)	// can't possibly satisfy
65		CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
66    if (subSampleCount > totalSubjects)	// reject attempt at sample stuffing
67		CssmError::throwMe(CSSM_ERRCODE_INVALID_SAMPLE_VALUE);
68#endif //STRICTCOUNTING
69
70    // evaluate
71    SublistValidationContext ctx(baseCtx, sample);
72    uint32 matched = 0;
73    for (uint32 n = 0; n < totalSubjects; n++) {
74		if ((matched += elements[n]->validate(ctx)) >= minimumNeeded)
75            return true;
76#ifdef STRICTCOUNTING
77        else if (matched + subSampleCount - n <= minimumNeeded)
78            return false;	// can't get there anymore
79#endif //STRICTCOUNTING
80    }
81	return false;
82}
83
84
85//
86// Make a copy of this subject in CSSM_LIST form
87//
88CssmList ThresholdAclSubject::toList(Allocator &alloc) const
89{
90	TypedList result(alloc, CSSM_ACL_SUBJECT_TYPE_THRESHOLD,
91		new(alloc) ListElement(minimumNeeded),
92		new(alloc) ListElement(totalSubjects));
93    for (uint32 n = 0; n < totalSubjects; n++)
94		result += new(alloc) ListElement(elements[n]->toList(alloc));
95	return result;
96}
97
98
99//
100// Create a ThresholdAclSubject
101//
102ThresholdAclSubject *ThresholdAclSubject::Maker::make(const TypedList &list) const
103{
104    // pick apart the input list
105    if (list.length() < 4)	// head + "n" + "k" + at least one subSubject
106        CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
107    uint32 minimumNeeded = getWord(list[1], 1);
108    uint32 totalSubjects = getWord(list[2], minimumNeeded);
109    if (list.length() != 3 + totalSubjects)
110        CssmError::throwMe(CSSM_ERRCODE_INVALID_ACL_SUBJECT_VALUE);
111
112    // now compile the subSubjects
113    AclSubjectVector elements(totalSubjects);
114    const ListElement *subSubject = &list[3];
115    for (uint32 n = 0; n < totalSubjects; n++, subSubject = subSubject->next())
116        elements[n] = ObjectAcl::make(subSubject->typedList());
117	return new ThresholdAclSubject(totalSubjects, minimumNeeded, elements);
118}
119
120ThresholdAclSubject *ThresholdAclSubject::Maker::make(Version, Reader &pub, Reader &priv) const
121{
122    Endian<uint32> totalSubjects; pub(totalSubjects);
123    Endian<uint32> minimumNeeded; pub(minimumNeeded);
124    AclSubjectVector subSubjects(totalSubjects);
125    for (uint32 n = 0; n < totalSubjects; n++)
126		subSubjects[n] = ObjectAcl::importSubject(pub, priv);
127	return new ThresholdAclSubject(totalSubjects, minimumNeeded, subSubjects);
128}
129
130ThresholdAclSubject::ThresholdAclSubject(uint32 n, uint32 k,
131    const AclSubjectVector &subSubjects)
132: SimpleAclSubject(CSSM_ACL_SUBJECT_TYPE_THRESHOLD),
133  minimumNeeded(k), totalSubjects(n), elements(subSubjects)
134{
135}
136
137
138//
139// Export the subject to a memory blob
140//
141template <class Action>
142void ThresholdAclSubject::exportBlobForm(Action &pub, Action &priv)
143{
144    pub(h2n(totalSubjects));
145    pub(h2n(minimumNeeded));
146    for (uint32 n = 0; n < totalSubjects; n++)
147		ObjectAcl::exportSubject(elements[n], pub, priv);
148}
149
150void ThresholdAclSubject::exportBlob(Writer::Counter &pub, Writer::Counter &priv)
151{ exportBlobForm(pub, priv); }
152
153void ThresholdAclSubject::exportBlob(Writer &pub, Writer &priv)
154{ exportBlobForm(pub, priv); }
155
156
157void ThresholdAclSubject::add(AclSubject *subject, unsigned beforePosition)
158{
159	secdebug("threshacl", "adding subject %p before position %u",
160		subject, beforePosition);
161	elements.insert(elements.begin() + beforePosition, subject);
162	totalSubjects++;
163}
164
165
166#ifdef DEBUGDUMP
167
168void ThresholdAclSubject::debugDump() const
169{
170	Debug::dump("Threshold(%u of %u)", minimumNeeded, totalSubjects);
171	for (unsigned int n = 0; n < elements.size(); n++) {
172		Debug::dump(" [");
173		if (Version v = elements[n]->version())
174			Debug::dump("V=%d ", v);
175		elements[n]->debugDump();
176		Debug::dump("]");
177	}
178}
179
180#endif //DEBUGDUMP
181