mono / mono

Mono open source ECMA CLI, C# and .NET implementation.
https://www.mono-project.com
Other
11.09k stars 3.82k forks source link

Security: Whitelisting assemblies permitted to make native p/invoke calls to deliver secure scripting. #14977

Open adv-sw opened 5 years ago

adv-sw commented 5 years ago

System overview : [native_app] <---> [trusted custom managed bootstrap assembly] <---> [untrusted managed app assemblies]

  1. I want our trusted managed bootstrap to be able to p/invoke to make wrapped native class calls into our trusted native app.
  2. I want untrusted managed apps to be blocked if they attempt to p/invoke anything.
  3. I want trusted system assemblies to be able to make p/invoke calls as necessary.

There should be a white list of assemblies which can p/invoke which should initially be configured to be all trusted system assemblies, which can be amended under C API control to configure mono runtime as desired.

Current Behavior: All assemblies can make p/invoke calls.

Expected Behavior: When running in secure mode, only trusted assemblies should be able to make p/invoke calls.

Assembly format feature required on :

.NET assemblies. Web Assembly.

Platforms : All.

Version: Feature not yet present.

As discussed on https://gitter.im/mono/mono today (11th June, 2019).

alexanderkyte commented 5 years ago

I think I've found the exact place to make a hacky version of this for the JIT/AOT backend. Interpreter would need it's own thing. Confirming now.

vargaz commented 5 years ago

The proper place to do this is probably an IL preprocessor, not the runtime.

alexanderkyte commented 5 years ago

I agree with @vargaz there that the highest quality way to surface this would be through the linker or something.

You can always break that though using SRE, so you need some kind of assertion at the very least.

alexanderkyte commented 5 years ago

Looks like you can filter here on the assembly that method belong to:

--- a/mono/mini/method-to-ir.c
+++ b/mono/mini/method-to-ir.c
@@ -7053,6 +7053,10 @@ mono_method_to_ir (MonoCompile *cfg, MonoMethod *method, MonoBasicBlock *start_b
                        } else if (fsig->pinvoke) {
                                MonoMethod *wrapper = mono_marshal_get_native_wrapper (cmethod, TRUE, cfg->compile_aot);
                                fsig = mono_method_signature_internal (wrapper);
+
+                               if (m_class_get_image (method->klass) != mono_defaults.corlib)
+                                       g_error (stderr, "JIT-making a call to %s from %s %s\n", cmethod->name, m_class_get_image (method->klass)->assembly_name, method->name);
+
                        } else if (constrained_class) {
                        } else {
                                fsig = mono_method_get_signature_checked (cmethod, image, token, generic_context, &cfg->error);
(END)
alexanderkyte commented 5 years ago

Which means that this mode would amount to:

  1. Having a ghashtable from MonoImage to gboolean that says whether each assembly loaded is allowed to have pinvokes. Could also add a field to MonoImage.
  2. Adding an option to driver.c that specifies this assertion
  3. Making interpreter use the lookup table added by 1 too
  4. Find out which assemblies we always want to enable other than corlib (may be just corlib).
adv-sw commented 5 years ago

Thanks guys. Appreciate the prompt attention you've given this.

Is extending this whitelist to block any other form of unsafe operation straightforward enough ?

If so, please add that to the spec.

adv-sw commented 5 years ago

Configuration on a per MonoDomain basis please. That delivers maximum flexibility without hopefully complicating anything. Thanks again, appreciating the rapid turnaround on this.

alexanderkyte commented 5 years ago

@adv-sw the question of how to expose the option to the end user is going to be 90% of the labor, if my triage of it seems accurate. How do you hope to use this?

adv-sw commented 5 years ago

We're developing a 3D web browser with mono/.net assembly as secure scripting engine, moving to web assembly at some point.

By default third party scripts will be secure - no p/invoke. no unsafe. Our bootstrap will be trusted & will be able to p/invoke as required for managed wrapper pattern. Hence whitelist required.

End users will be able to trust third party scripts to perform unsafe operations in the same way they can trust unmanaged code plugins if they want to, but they'll have to opt into this - globally or on a case by case basis, via UI that we'll provide.

We're new so we don't have legacy install bases that'll break. You'll have to leave safe mode turned off by default for existing apps so they don't break & their developers will have to enable & integrate as they see fit going forwards.

Hope that answers the question. We need mono secure to use it as a next-gen web scripting engine. The web is secure by default.

I intend to integrate the moment you have a working build I can compile, so you'll gain immediate feedback as to whether the solution we seek has been delivered.

Once again, sincere thanks.

alexanderkyte commented 5 years ago

I think it makes sense then to add this as a configure flag that disables it by default, and then have a short list of allowed images.

I would do this:

  1. Add configure flag that has a disable around the line from the comment above
  2. Run it and log and see what has to be enabled for the BCL test harness.
  3. Disable all other images from producing pinvokes by adding to the line from the comment above an early exit. This ensures that the method doesn't compile. If it doesn't compile, it can't run. That ensures the invariant is enforced by the runtime.
  4. Add logging error messages when we do that early exit
  5. Add icall to Mono.Runtime namespace to add a new image to the list. Or take input at compile-time another way. This is so you can add any pinvokes you need for your app.
adv-sw commented 5 years ago

This ensures that the method doesn't compile. If it doesn't compile, it can't run You mean from the intermediate language I assume. Other compilers can compile to intermediate without the safeguard you propose otherwise.

Logging should be an exception so runtime can know assembly wanted to do something it's blocked from doing so app can inform user "app tried to do x do you want to let it" or similar.