12

Does anyone have experience with F# code in partial trust scenarios? As in, creating assemblies with [<AllowPartiallyTrustedCallers>]?

I am working on a couple of projects that we need to be able to run in partial trust, and we have been trying to use Level 2 Security Rules (http://msdn.microsoft.com/en-us/library/dd233102.aspx). In practice for our self-contained assemblies this is easy - just put an attribute; but sometimes our assemblies reference third-party DLLs that are not annotated and assumed "SecurityCritical." This is where it gets "interesting."

Having worked with it for past couple of days there appears to be a serious issue with F#. .NET security policy expects you to annotate types/methods with [<SecuritySafeCritical>] if they reference or call "SecurityCritical" code, which happens to be most of the code out there on NuGet because this is what it defaults to. Now, in F# this works ok until you start using closures. You cannot do:

namespace Foo

open System.Security

[<assembly: AllowPartiallyTrustedCallers>]
[<assembly: SecurityRules(SecurityRuleSet.Level2)>]
do()

[<SecurityCritical>]
module C =
    let get () = [ 1 .. 10 ]

[<SecuritySafeCritical>]
module M =

    let foo () =
        seq {
            for i in 1 .. 10 do
                yield!
                    C.get ()
                    |> Seq.filter (fun x -> x % 2 = 0)
        }

This assembly fails to pass SecAnnotate.exe checks because F# compiler lifts the closure to a separate type, which is now NOT annotated with [<SecuritySafeCritical>], defaults to Transparent, but references some critical code, which is an error.

It sounds like a minor restriction but it cost me many hours altering code to avoid closures and satisfy SecAnnotate constraints. Maybe F# could propagate security attributes to closure types it creates? Is there another simple way around this that I am missing?

t0yv0
  • 4,714
  • 19
  • 36

1 Answers1

6

You can apply SecurityCritical as an assembly-level attribute:

[<assembly: SecurityCritical>]

A better approach though, assuming you're just writing a "plain" F# assembly -- i.e., one that isn't doing anything which requires special security (e.g., P/Invoke) -- would be to replace:

[<assembly: AllowPartiallyTrustedCallers>]

with

[<assembly: SecurityTransparent>]

The MSDN page for SecurityTransparentAttribute says:

Specifies that an assembly cannot cause an elevation of privilege.

Transparent assemblies can be accessed from partially trusted code and cannot expose access to any protected resources or functionality. Code in the assembly is not allowed to suppress code access security checks and cannot cause an elevation of privilege.

The F# 3.0 version of FSharp.Core also uses this attribute for the same reason.

Links to additional information:

Jack P.
  • 11,487
  • 1
  • 29
  • 34
  • Thanks for the links! Unfortunately in my scenario I am trying to enable partially trusted code to call third-party `SecurityCriticalCode` (un-annotated code), so seems like I *do* need APTCA + `[]`, at least somewhere.. – t0yv0 Jul 12 '13 at 16:25
  • You might be able to use `[]` then mark any methods/functions which call into your third-party assembly with `[]`. I think that'll work in most partial trust situations, except for Silverlight. – Jack P. Jul 12 '13 at 17:27
  • Once you mark an assembly as SecurityTransparent, you cannot use SecuritySafeCritical anymore - try with SecAnnotate.exe, it complains. Therefore, assembly has to be APTCA. But thanks anyway, I guess I am on the right track, will just have to live with the unnecessary complications F# makes for doing this. – t0yv0 Jul 12 '13 at 18:03
  • 1
    Hmm. My suggestion then, is to break out the *BFG9000* and write an `*.fsx` script which uses Mono.Cecil to analyze your compiled assembly then automatically annotate the appropriate methods with `[]`. That way, you'd avoid the need to write your code in some special way or hand-annotate the code every time you build it. – Jack P. Jul 12 '13 at 18:13