2 * Copyright (c) 2000-2004,2006,2011,2014 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
26 // acl_threshold - Threshold-based group ACL subjects
28 #include <security_cdsa_utilities/acl_threshold.h>
30 #include <security_utilities/endian.h>
34 // Validate a credential set against this subject.
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
42 class SublistValidationContext
: public AclValidationContext
{
44 SublistValidationContext(const AclValidationContext
&ctx
, const TypedList
&list
)
45 : AclValidationContext(ctx
), sampleList(list
) { }
47 uint32
count() const { return sampleList
.length() - 1; }
48 const TypedList
&sample(uint32 n
) const
49 { return TypedList::overlay(sampleList
[n
+1].list()); }
51 void matched(const TypedList
*) const { } //@@@ ignore sub-matches for now
53 const TypedList
&sampleList
;
56 bool ThresholdAclSubject::validates(const AclValidationContext
&baseCtx
,
57 const TypedList
&sample
) const
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
71 SublistValidationContext
ctx(baseCtx
, sample
);
73 for (uint32 n
= 0; n
< totalSubjects
; n
++) {
74 if ((matched
+= elements
[n
]->validates(ctx
)) >= minimumNeeded
)
77 else if (matched
+ subSampleCount
- n
<= minimumNeeded
)
78 return false; // can't get there anymore
79 #endif //STRICTCOUNTING
86 // Make a copy of this subject in CSSM_LIST form
88 CssmList
ThresholdAclSubject::toList(Allocator
&alloc
) const
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
));
100 // Create a ThresholdAclSubject
102 ThresholdAclSubject
*ThresholdAclSubject::Maker::make(const TypedList
&list
) const
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
);
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
);
120 ThresholdAclSubject
*ThresholdAclSubject::Maker::make(Version
, Reader
&pub
, Reader
&priv
) const
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
);
130 ThresholdAclSubject::ThresholdAclSubject(uint32 n
, uint32 k
,
131 const AclSubjectVector
&subSubjects
)
132 : SimpleAclSubject(CSSM_ACL_SUBJECT_TYPE_THRESHOLD
),
133 minimumNeeded(k
), totalSubjects(n
), elements(subSubjects
)
139 // Export the subject to a memory blob
141 template <class Action
>
142 void ThresholdAclSubject::exportBlobForm(Action
&pub
, Action
&priv
)
144 pub(h2n(totalSubjects
));
145 pub(h2n(minimumNeeded
));
146 for (uint32 n
= 0; n
< totalSubjects
; n
++)
147 ObjectAcl::exportSubject(elements
[n
], pub
, priv
);
150 void ThresholdAclSubject::exportBlob(Writer::Counter
&pub
, Writer::Counter
&priv
)
151 { exportBlobForm(pub
, priv
); }
153 void ThresholdAclSubject::exportBlob(Writer
&pub
, Writer
&priv
)
154 { exportBlobForm(pub
, priv
); }
157 void ThresholdAclSubject::add(AclSubject
*subject
, unsigned beforePosition
)
159 secinfo("threshacl", "adding subject %p before position %u",
160 subject
, beforePosition
);
161 elements
.insert(elements
.begin() + beforePosition
, subject
);
168 void ThresholdAclSubject::debugDump() const
170 Debug::dump("Threshold(%u of %u)", minimumNeeded
, totalSubjects
);
171 for (unsigned int n
= 0; n
< elements
.size(); n
++) {
173 if (Version v
= elements
[n
]->version())
174 Debug::dump("V=%d ", v
);
175 elements
[n
]->debugDump();