1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339
|
//-----------------------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
namespace System.Activities.Runtime
{
using System;
using System.Reflection;
using System.Runtime.Serialization;
using System.Diagnostics.CodeAnalysis;
using System.Runtime;
using System.Security;
using System.Security.Permissions;
using System.Threading;
[DataContract]
class CallbackWrapper
{
static BindingFlags bindingFlags = BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Static;
static PermissionSet ReflectionMemberAccessPermissionSet = null;
string callbackName;
string declaringAssemblyName;
string declaringTypeName;
Delegate callback;
ActivityInstance activityInstance;
public CallbackWrapper(Delegate callback, ActivityInstance owningInstance)
{
this.ActivityInstance = owningInstance;
this.callback = callback;
}
public ActivityInstance ActivityInstance
{
get
{
return this.activityInstance;
}
private set
{
this.activityInstance = value;
}
}
protected bool IsCallbackNull
{
get
{
return this.callback == null && this.callbackName == null;
}
}
protected Delegate Callback
{
get
{
return this.callback;
}
}
[DataMember(Name = "callbackName")]
internal string SerializedCallbackName
{
get { return this.callbackName; }
set { this.callbackName = value; }
}
[DataMember(EmitDefaultValue = false, Name = "declaringAssemblyName")]
internal string SerializedDeclaringAssemblyName
{
get { return this.declaringAssemblyName; }
set { this.declaringAssemblyName = value; }
}
[DataMember(EmitDefaultValue = false, Name = "declaringTypeName")]
internal string SerializedDeclaringTypeName
{
get { return this.declaringTypeName; }
set { this.declaringTypeName = value; }
}
[DataMember(Name = "ActivityInstance")]
internal ActivityInstance SerializedActivityInstance
{
get { return this.ActivityInstance; }
set { this.ActivityInstance = value; }
}
public static bool IsValidCallback(Delegate callback, ActivityInstance owningInstance)
{
Fx.Assert(callback != null, "This should only be called with non-null callbacks");
object target = callback.Target;
// if the target is null, it is static
if (target == null)
{
Fx.Assert(callback.Method.IsStatic, "This method should be static when target is null");
return true;
}
// its owner's activity
if (object.ReferenceEquals(target, owningInstance.Activity))
{
return true;
}
return false;
}
// Special note about establishing callbacks:
//
// When establising a callback, we need to Assert ReflectionPermission(MemberAccess) because the callback
// method will typically be a private method within a class that derives from Activity. Activity authors need
// to be aware that their callback may be invoked with different permissions than were present when the Activity
// was originally executed and perform appropriate security checks.
//
// We ensure that the declaring type of the callback method derives from Activity. This check is made in RecreateCallback.
//
// The classes that derive from CallbackWrapper and call EnsureCallback do an explicit cast of the returned delegate
// to the delegate type that they expect before calling thru to the delegate. This cast is done in SecuritySafeCritical code.
//
// These checks are both made in Security[Safe]Critical code.
[Fx.Tag.SecurityNote(Critical = "Because we are calling GenerateCallback, which are SecurityCritical.")]
[SecurityCritical]
protected void EnsureCallback(Type delegateType, Type[] parameterTypes, Type genericParameter)
{
// We were unloaded and have some work to do to rebuild the callback
if (this.callback == null)
{
this.callback = GenerateCallback(delegateType, parameterTypes, genericParameter);
Fx.Assert(this.callback != null, "GenerateCallback should have been able to produce a non-null callback.");
}
}
[Fx.Tag.SecurityNote(Critical = "Because we are calling GenerateCallback, which are SecurityCritical.",
Safe = "Because the delegate is not leaked out of this routine. It is only validated.")]
[SecuritySafeCritical]
protected void ValidateCallbackResolution(Type delegateType, Type[] parameterTypes, Type genericParameter)
{
Fx.Assert(this.callback != null && this.callbackName != null, "We must have a callback and a callback name");
if (!this.callback.Equals(GenerateCallback(delegateType, parameterTypes, genericParameter)))
{
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidExecutionCallback(this.callback.Method, null)));
}
}
MethodInfo FindMatchingGenericMethod(Type declaringType, Type[] parameterTypes, Type genericParameter)
{
MethodInfo[] potentialMatches = declaringType.GetMethods(bindingFlags);
for (int i = 0; i < potentialMatches.Length; i++)
{
MethodInfo potentialMatch = potentialMatches[i];
if (potentialMatch.IsGenericMethod && potentialMatch.Name == this.callbackName)
{
Fx.Assert(potentialMatch.IsGenericMethodDefinition, "We should be getting the generic method definition here.");
Type[] genericArguments = potentialMatch.GetGenericArguments();
if (genericArguments.Length == 1)
{
potentialMatch = potentialMatch.MakeGenericMethod(genericParameter);
ParameterInfo[] parameters = potentialMatch.GetParameters();
bool match = true;
for (int parameterIndex = 0; parameterIndex < parameters.Length; parameterIndex++)
{
ParameterInfo parameter = parameters[parameterIndex];
if (parameter.IsOut || parameter.IsOptional || parameter.ParameterType != parameterTypes[parameterIndex])
{
match = false;
break;
}
}
if (match)
{
return potentialMatch;
}
}
}
}
return null;
}
[Fx.Tag.SecurityNote(Critical = "Because we are calling RecreateCallback, which is SecurityCritical.")]
[SecurityCritical]
Delegate GenerateCallback(Type delegateType, Type[] parameterTypes, Type genericParameter)
{
Type declaringType;
MethodInfo methodInfo = GetMatchingMethod(parameterTypes, out declaringType);
if (methodInfo == null)
{
Fx.Assert(declaringType != null, "We must have found the declaring type.");
methodInfo = FindMatchingGenericMethod(declaringType, parameterTypes, genericParameter);
}
if (methodInfo == null)
{
return null;
}
return RecreateCallback(delegateType, methodInfo);
}
[Fx.Tag.SecurityNote(Critical = "Because we are calling RecreateCallback, which is SecurityCritical.")]
[SecurityCritical]
protected void EnsureCallback(Type delegateType, Type[] parameters)
{
// We were unloaded and have some work to do to rebuild the callback
if (this.callback == null)
{
Type unusedDeclaringType;
MethodInfo methodInfo = GetMatchingMethod(parameters, out unusedDeclaringType);
Fx.Assert(methodInfo != null, "We must have a method info by now");
this.callback = RecreateCallback(delegateType, methodInfo);
}
}
MethodInfo GetMatchingMethod(Type[] parameters, out Type declaringType)
{
Fx.Assert(this.callbackName != null, "This should only be called when there is actually a callback to run.");
object targetInstance = this.ActivityInstance.Activity;
if (this.declaringTypeName == null)
{
declaringType = targetInstance.GetType();
}
else
{
// make a MethodInfo since it's not hanging directly off of our activity type
Assembly callbackAssembly;
if (this.declaringAssemblyName != null)
{
callbackAssembly = Assembly.Load(this.declaringAssemblyName);
}
else
{
callbackAssembly = targetInstance.GetType().Assembly;
}
declaringType = callbackAssembly.GetType(this.declaringTypeName);
}
Fx.Assert(declaringType != null, "declaring type should be re-constructable from our serialized components");
return declaringType.GetMethod(this.callbackName, bindingFlags, null, parameters, null);
}
// The MethodInfo passed to this method must be derived
[Fx.Tag.SecurityNote(Critical = "Because we are Asserting ReflectionPermission(MemberAccess) in order to get at private callback methods.")]
[SecurityCritical]
Delegate RecreateCallback(Type delegateType, MethodInfo callbackMethod)
{
object targetInstance = null;
// If the declaring type does not derive from Activity, somebody has manipulated the callback in the persistece store.
if (!typeof(Activity).IsAssignableFrom(callbackMethod.DeclaringType))
{
return null;
}
if (!callbackMethod.IsStatic)
{
targetInstance = this.ActivityInstance.Activity;
}
// Asserting ReflectionPermission.MemberAccess because the callback method is most likely internal or private
if (ReflectionMemberAccessPermissionSet == null)
{
PermissionSet myPermissionSet = new PermissionSet(PermissionState.None);
myPermissionSet.AddPermission(new ReflectionPermission(ReflectionPermissionFlag.MemberAccess));
Interlocked.CompareExchange(ref ReflectionMemberAccessPermissionSet, myPermissionSet, null);
}
ReflectionMemberAccessPermissionSet.Assert();
try
{
return Delegate.CreateDelegate(delegateType, targetInstance, callbackMethod);
}
finally
{
CodeAccessPermission.RevertAssert();
}
}
[OnSerializing]
[SuppressMessage(FxCop.Category.Usage, FxCop.Rule.ReviewUnusedParameters)]
[SuppressMessage(FxCop.Category.Usage, "CA2238:ImplementSerializationMethodsCorrectly",
Justification = "Needs to be internal for serialization in partial trust. We have set InternalsVisibleTo(System.Runtime.Serialization) to allow this.")]
internal void OnSerializing(StreamingContext context)
{
if (this.callbackName == null && !this.IsCallbackNull)
{
MethodInfo method = this.callback.Method;
this.callbackName = method.Name;
Type declaringType = method.DeclaringType;
Type activityType = this.ActivityInstance.Activity.GetType();
if (declaringType != activityType)
{
// If we're not directly off of the Activity type being used,
// then we need to store the declaringType's name.
this.declaringTypeName = declaringType.FullName;
if (declaringType.Assembly != activityType.Assembly)
{
this.declaringAssemblyName = declaringType.Assembly.FullName;
}
}
if (method.IsGenericMethod)
{
OnSerializingGenericCallback();
}
}
}
protected virtual void OnSerializingGenericCallback()
{
// Generics are invalid by default
throw FxTrace.Exception.AsError(new InvalidOperationException(SR.InvalidExecutionCallback(this.callback.Method, null)));
}
}
}
|