Skip to content

Prevent getenv and trace file-system for security applications#550

Closed
lefessan wants to merge 1 commit intoocaml:trunkfrom
lefessan:2016-04-18-secur-ocaml
Closed

Prevent getenv and trace file-system for security applications#550
lefessan wants to merge 1 commit intoocaml:trunkfrom
lefessan:2016-04-18-secur-ocaml

Conversation

@lefessan
Copy link
Contributor

The following PR provides two features:

  • a configure option --no-getenv that prevents the runtime from using getenv (Sys.getenv is still working normally). It can be used to compile an application in a security context, to prevent attackers from using the env. variables used by the runtime to temper the application behavior (debugger socket, gc tuning, etc.)
  • the ability to load C plugins, specified in a CAML_CPLUGINS env variable, for example to monitor calls to some system calls in a security context (they could also be used to add setup GC hooks for example). This ability can be disabled using a new configure option --no-cplugins (and of course, it is disabled by --no-getenv).

@lefessan
Copy link
Contributor Author

I forgot another application of C plugins: they can be used to "virtualize" an application from the file-system, i.e. redirect open, unlink, rename, etc. to other files, although it is not its main purpose.

@lefessan lefessan force-pushed the 2016-04-18-secur-ocaml branch from fa9eae8 to 1347dd8 Compare April 18, 2016 12:55
@gasche
Copy link
Member

gasche commented Apr 18, 2016

I understand the CAML_GETENV part and it looks fine to me. The other part is complex, I have not reviewed it. Maybe it would make sense to split the PR in two? I wouldn't mind merging the GETENV part in 4.04+dev.

@lefessan
Copy link
Contributor Author

I am still working on the CAML_CPLUGINS part anyway, it seems not to work correctly because asmrun files are not compiled in -fPIC by default...

@lefessan lefessan force-pushed the 2016-04-18-secur-ocaml branch from 1347dd8 to 6744baa Compare April 18, 2016 16:23
@lefessan
Copy link
Contributor Author

Ok, the new version works both with bytecode and native code.

@gasche As it is a runtime thing, I am planning to ask @damiendoligez for both, since he is the author of most the recommendations of LafoSec :-)

@gasche
Copy link
Member

gasche commented Apr 18, 2016

I think it would still be nicer to split the two separate features in two separate commits.

@avsm
Copy link
Member

avsm commented Apr 18, 2016

these seem like quite distinct features -- the getenv one is quite useful for Mirage/Xen since we can supply an alternative macro that supplies the runtime configuration information from XenStore (right now we have to define a custom getenv stub).

@dbuenzli
Copy link
Contributor

(right now we have to define a custom getenv stub).

Wouldn't you do this anyway (for other getenvs that may lurk in e.g. C library code) ?

@avsm
Copy link
Member

avsm commented Apr 18, 2016

Wouldn't you do this anyway (for other getenvs that may lurk in e.g. C library code)

Preference is to remove getenv and fix up the C libraries where possible. This may be impractical, but slightly less so now that the OCaml runtime can be configured to not trigger a zillion dynamic getenv calls.

@lefessan lefessan force-pushed the 2016-04-18-secur-ocaml branch from 6744baa to 02e3106 Compare April 19, 2016 08:24
@mshinwell
Copy link
Contributor

@lefessan If you're going to split the plugins part off, could I suggest you also make a third pull request which does something like adding a "configure" option for PIC, which does the right thing (i.e. C compiler flags etc + the asmrun/Makefile change that is required)? (We also need this for the gdb plugin.)

@damiendoligez damiendoligez added this to the 4.04 milestone Apr 19, 2016
@lefessan
Copy link
Contributor Author

As a proof-of-concept, here is an experimental set of plugins:

https://github.com/OCamlPro/ocp-cplugins

For example, the ocp-show-build -o build.log -- make followed by grep ocamlopt.opt build.log was particularly useful to understand how to translate the multiple calls to ocamlbuild in dose into ocp-build format...

@lefessan
Copy link
Contributor Author

@mshinwell I am not an expert of configure scripts (and even less if it is confirmed that we are moving to autoconf), so I will leave the task of adding -fPIC correctly to a better expert. The current behavior is enough for the current purpose (but would clearly benefit from -fPIC for more uses).

@lefessan lefessan force-pushed the 2016-04-18-secur-ocaml branch from 02e3106 to 523574b Compare April 29, 2016 20:22
@DemiMarie
Copy link
Contributor

I would prefer if --getenv were instead a compile-time option. This would require that it be stored in the bytecode files generated by ocamlc and in the startup of programs generated by ocamlopt.

@gasche
Copy link
Member

gasche commented Apr 30, 2016

@drbo what would be the semantics of linking together some modules compiled with --no-getenv and some with getenv enabled? I'm not sure it is easy to implement a per-module semantics (do compiled C stubs have some form of private per-module register?).

@damiendoligez
Copy link
Member

@drbo, @lefessan The no-getenv option could also be a runtime variant, so you could choose it at link time.

@lefessan
Copy link
Contributor Author

From what I understood at the core-dev meeting, there was not a strong interest in this patch, but neither any opposition. Since it is interesting for us, I will rebase it, add access to a few more variables/functions (especially hooks), and merge it in the next weeks.

@lefessan
Copy link
Contributor Author

I am not fond of runtime variants. OPAM provides the possibility to have multiple switches, I find it better to provide a compiler with --no-getenv enabled than to increase the number of variants.

@alainfrisch
Copy link
Contributor

With N different configure-time flags (no-getenv, fp, afl instrumentation, ...), will we have 2^N OPAM switches? Can switches be created "lazily" on the user machine (based on some configure-time options)?

@lefessan
Copy link
Contributor Author

lefessan commented Jun 17, 2016

I wouldn't actually create an ocaml-no-getenv switch, but an ocaml-secure switch, with all the configure arguments for maximum security enabled.

Can switches be created "lazily" on the user machine (based on some configure-time options)?

Not sure I understand your question, but a possible answer would be to modify the configure script to read an OCAML_CONFIGURE env variable, that you could be used to create variants from the standard switch with specific arguments. I will leave that as an exercise for the reader (I have already added too many env variables to OCaml for one lifetime).

@alainfrisch
Copy link
Contributor

I was thinking about supporting that in OPAM. You could do opam switch 4.03/blabla --configure "--no-get-env --fp". This would create an internal switch based on upstream 4.03, but with extra configure options.

@lefessan
Copy link
Contributor Author

It's probably worth submitting an issue on opam. Maybe it is already possible to do it. Instead of opam switch 4.03/blabla --configure "--no-get-env --fp", I would probably set a variable, that would then be available to all the packages in the switch (especially as compilers are now standard packages in 2.0): opam switch 4.03/blabla --set "configure_args=--no-get-env --fp"

@lefessan
Copy link
Contributor Author

lefessan commented Jun 17, 2016

Oh, but then you would need also a way to "unsplit" the variables in the command line:

build: [
   "./configure" unsplit(configure_args}
   make "world.opt"
]

@AltGr

@avsm
Copy link
Member

avsm commented Jun 17, 2016

I don't understand what's wrong with using runtime variants as @damiendoligez suggests -- the "no getenv" feature is purely constrained to affecting libasmrun.a and doesn't touch the OCaml compilation units. We already have all the compiler CLI options required to support it present, so it doesn't add any complexity to the build systems.

This would save the (much more heavyweight) option of a new compiler flag for features that absolutely require them since they affect OCaml compilation units (like AFL fuzzing, or flambda).

@lefessan
Copy link
Contributor Author

I don't understand what's wrong with using runtime variants as @damiendoligez suggests

Because, for security reasons, you want all your toolchain to be unaffected by environment variables. Suppose you have a completely scripted build system, that cannot be modified, but you can change env variables before starting it. Then, an engineer can set an env. variable to connect to the OCaml compiler during the build, and inject some code in the generated object, that will be linked in the executable at the end. That's the kind of security holes that ANSSI (French equivalent of NSA) is trying to prevent.

I don't see how you can prevent that with runtime variants, unless you actually set OCAMLPARAM=runtime-variant=n which defeats the whole purpose of the patch...

PS: a good complement would be to disable OCAMLPARAM, OCAMLLIB and other env variables on the OCaml side when --no-getenv is specified.

@avsm
Copy link
Member

avsm commented Jun 17, 2016

Because, for security reasons, you want all your toolchain to be unaffected by environment variables

A configure option to disable the installation of the standard runtime library (and debug version) would work here, wouldn't it? Presumably any such locked down OCaml distribution would have its own distribution system, and this could choose which runtime variants to install as a matter of local security policy.

@lefessan
Copy link
Contributor Author

What if the attack happens during the compilation of OCaml itself ? Code could be injected inside stdlib for example, while the standard runtime variant is still being used. If we add an option to configure to change that, I don't see the benefit over the --no-getenv option. Or maybe, we should add --secure option, that will trigger the no-getenv behavior, but also other features for security.

@avsm
Copy link
Member

avsm commented Jun 17, 2016

What if the attack happens during the compilation of OCaml itself ?

I'm afraid I don't understand your threat model. Presumably you would build your toolchain in a deterministic environment (e.g. a locked down Docker container with tight seccomp allowances, and a trusted kernel), and provide that as a binary toolchain for untrusted code to be compiled.

Or maybe, we should add --secure option, that will trigger the no-getenv behavior, but also other features for security.

One person's --secure is another person's --broken (if they actually want to be able to dynamically change parameters, as most developers will want to).

@lefessan
Copy link
Contributor Author

as a binary toolchain for untrusted code to be compiled.

The threat model is not to compile untrusted code, but untrusted engineers who would want to inject their untrusted code in a trusted code. In such a setting, everything should be static... but most developers won't want to work in such an environment !

@damiendoligez
Copy link
Member

Or maybe, we should add --secure option, that will trigger the no-getenv behavior, but also other features for security.

+1 to that.-not-getenv sounds like it's only a tiny part of the solution.

As for merging soon, lack of interest is not enough, you need at least a code review. You also need approval from another core developer, unless the patch is really small (which it isn't in its current state).

I have one question about the patch: why don't you #define the getenv identifier directly instead of changing the calls in the source ? That would make it more robust to future additions.
Same question for the other syscalls.

Finally, I really think this should be two separate PRs, as the tracing stuff has nothing to do with security.

@lefessan
Copy link
Contributor Author

+1 to that.-not-getenv sounds like it's only a tiny part of the solution.
Finally, I really think this should be two separate PRs, as the tracing stuff has nothing to do with security.

The rationale was that the addition of C plugins was creating a security hole (well, not bigger than the current debugger support), and that the --no-getenv argument would provide a way to disable it. Since everybody here seems to think it's not the case, I will drop the --no-getenv part of the patch and wait for a bigger fix for security.

As for merging soon, lack of interest is not enough, you need at least a code review. You also need approval from another core developer, unless the patch is really small (which it isn't in its current state).

I will see if Pierre can do it.

I have one question about the patch: why don't you #define the getenv identifier directly instead of changing the calls in the source ? That would make it more robust to future additions.
Same question for the other syscalls.

I think it's really a bad practice to #define the names of existing functions: it makes code hard to debug because other developers don't understand why a call, to a C function they know, does not behave as usual. Moreover, you also want to control when the macro should be used, and when it shouldn't.

@damiendoligez
Copy link
Member

I think it's really a bad practice to #define the names of existing functions: it makes code hard to debug because other developers don't understand why a call, to a C function they know, does not behave as usual. Moreover, you also want to control when the macro should be used, and when it shouldn't.

Good arguments, but for security you want to make sure the macros are always used, right?

@lefessan
Copy link
Contributor Author

Here, the idea was to prevent getenv calls done by the runtime, without the OCaml part asking for them, i.e. Sys.getenv would still work, even if --no-getenv would be set.


This function can then be used to query the address of other symbols
in the executable, limited to the following queries:
*/
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This explanation is really unclear: there are two variables called query, and I don't understand what the two queries do.

@lefessan
Copy link
Contributor Author

lefessan commented Jul 6, 2016

Replaced by #668

@lefessan lefessan closed this Jul 6, 2016
@lefessan lefessan deleted the 2016-04-18-secur-ocaml branch January 24, 2021 16:08
mshinwell pushed a commit to mshinwell/ocaml that referenced this pull request Jul 12, 2021
The newer-version-of relation is entirely orthogonal to
inlinability, so `meet` and `join` should cause inlinability to be
recomputed.
EduardoRFS pushed a commit to esy-ocaml/ocaml that referenced this pull request Jul 23, 2021
stedolan added a commit to stedolan/ocaml that referenced this pull request Mar 22, 2022
stedolan added a commit to stedolan/ocaml that referenced this pull request May 24, 2022
stedolan added a commit to stedolan/ocaml that referenced this pull request May 24, 2022
64235a3 flambda-backend: Change Float.nan from sNaN to qNaN (ocaml#466)
14a8e27 flambda-backend: Track GC work for all managed bigarray allocations (upstream 11022) (ocaml#569)
c3cda96 flambda-backend: Add two new methods to targetint for dwarf (ocaml#560)
e6f1fed flambda-backend: Handle arithmetic overflow in select_addr (ocaml#570)
dab7209 flambda-backend: Add Target_system to ocaml/utils (ocaml#542)
82d5044 flambda-backend: Enhance numbers.ml with more primitive types (ocaml#544)
216be99 flambda-backend: Fix flambda_o3 and flambda_oclassic attributes (ocaml#536)
4b56e07 flambda-backend: Test naked pointer root handling (ocaml#550)
40d69ce flambda-backend: Stop local function optimisation from moving code into function bodies; opaque_identity fixes for class compilation (ocaml#537)
f08ae58 flambda-backend: Implemented inlining history and use it inside inlining reports (ocaml#365)
ac496bf flambda-backend: Disable the local keyword in typing (ocaml#540)
7d46712 flambda-backend: Bugfix for Typedtree generation of arrow types (ocaml#539)
61a7b47 flambda-backend: Insert missing page table check in roots_nat.c (ocaml#541)
323bd36 flambda-backend: Compiler error when -disable-all-extensions and -extension are used (ocaml#534)
d8956b0 flambda-backend: Persistent environment and reproducibility (ocaml#533)
4a0c89f flambda-backend: Revert "Revert bswap PRs (480 and 482)" (ocaml#506)
7803705 flambda-backend: Cause a C warning when CAMLreturn is missing in C stubs. (ocaml#376)
6199db5 flambda-backend: Improve unboxing during cmm for Flambda (ocaml#295)
96b9e1b flambda-backend: Print diagnostics at runtime for Invalid (ocaml#530)
42ab88e flambda-backend: Disable bytecode compilers in ocamltest (ocaml#504)
58c72d5 flambda-backend: Backport ocaml#10595 from upstream/trunk (ocaml#471)
1010539 flambda-backend: Use C++ name mangling convention (ocaml#483)
81881bb flambda-backend: Local allocation test no longer relies on lifting (ocaml#525)
f5c4719 flambda-backend: Fix an assertion in Closure that breaks probes (ocaml#505)
c2cf2b2 flambda-backend: Add some missing command line arguments to ocamlnat (ocaml#499)

git-subtree-dir: ocaml
git-subtree-split: 64235a3
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants