$ python3 -c "from samba import ldb;a = ldb.Ldb(); ldb.Dn(a, '<>').canonical_str()" Segmentation fault (core dumped) This gets beyond the Python bindings. I think the problem is there are no components, but ldb missed that because it expected there were extended components, or something. #0 __strcasecmp_l_avx () at ../sysdeps/x86_64/multiarch/strcmp-sse42.S:270 #1 0x00007ffff73c8b53 in ldb_dn_canonical (mem_ctx=0x9a7f50, dn=0x9a7f50, ex_format=0) at ../../lib/ldb/common/ldb_dn.c:1818 #2 0x00007ffff73c8d75 in ldb_dn_canonical_string (mem_ctx=0x9a7f50, dn=0x9a7f50) at ../../lib/ldb/common/ldb_dn.c:1865 #3 0x00007ffff73f1e4e in py_ldb_dn_canonical_str (self=0x7ffff7597650, _unused_ignored=0x0) at ../../lib/ldb/pyldb.c:510 #4 0x00000000004d7f6a in _PyMethodDef_RawFastCallKeywords (kwnames=<optimized out>, nargs=<optimized out>, args=<optimized out>, self=<ldb.Dn at remote 0x7ffff7597650>, method=0x7ffff74041a0 <py_ldb_dn_methods+192>) at ../Objects/call.c:629 #5 _PyMethodDescr_FastCallKeywords (descrobj=<method_descriptor at remote 0x7ffff74355a0>, args=0x7ffff75a2b68, nargs=<optimized out>, kwnames=<optimized out>) at ../Objects/descrobject.c:288 #6 0x0000000000552313 in call_function (kwnames=0x0, oparg=1, pp_stack=0x7fffffffb0c0) at ../Python/ceval.c:4593 #7 _PyEval_EvalFrameDefault (f=<optimized out>, throwflag=<optimized out>) at ../Python/ceval.c:3110 #8 0x000000000054b9f2 in PyEval_EvalFrameEx (throwflag=0, f=Frame 0x7ffff75a29f8, for file <string>, line 1, in <module> ()) at ../Python/ceval.c:547 #9 _PyEval_EvalCodeWithName (_co=<optimized out>, globals=<optimized out>, locals=<optimized out>, args=<optimized out>, argcount=<optimized out>, kwnames=0x0, kwargs=0x0, kwcount=<optimized out>, kwstep=2, defs=0x0, defcount=0, kwdefs=0x0, closure=0x0, name=0x0, qualname=0x0) at ../Python/ceval.c:3930 #10 0x000000000053109f in PyEval_EvalCodeEx (closure=0x0, kwdefs=0x0, defcount=0, defs=0x0, kwcount=0, kws=0x0, argcount=0, args=0x0, locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x8deba8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff75f9c28>, 'ldb': <module at remote 0x7ffff743a098>, 'a': <ldb.Ldb at remote 0x7ffff7584950>}, globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x8deba8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff75f9c28>, 'ldb': <module at remote 0x7ffff743a098>, 'a': <ldb.Ldb at remote 0x7ffff7584950>}, _co=<code at remote 0x7ffff74c55d0>) at ../Python/ceval.c:3959 #11 PyEval_EvalCode ( locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x8deba8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff75f9c28>, 'ldb': <module at remote 0x7ffff743a098>, 'a': <ldb.Ldb at remote 0x7ffff7584950>}, globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x8deba8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff75f9c28>, 'ldb': <module at remote 0x7ffff743a098>, 'a': <ldb.Ldb at remote 0x7ffff7584950>}, co=<code at remote 0x7ffff74c55d0>) at ../Python/ceval.c:524 #12 run_mod (arena=0x7ffff76002a0, flags=0x7fffffffb2ec, locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x8deba8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff75f9c28>, 'ldb': <module at remote 0x7ffff743a098>, 'a': <ldb.Ldb at remote 0x7ffff7584950>}, globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x8deba8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff75f9c28>, 'ldb': <module at remote 0x7ffff743a098>, 'a': <ldb.Ldb at remote 0x7ffff7584950>}, filename='<string>', mod=<optimized out>) at ../Python/pythonrun.c:1035 #13 PyRun_StringFlags (str=<optimized out>, start=257, globals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x8deba8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff75f9c28>, 'ldb': <module at remote 0x7ffff743a098>, 'a': <ldb.Ldb at remote 0x7ffff7584950>}, locals={'__name__': '__main__', '__doc__': None, '__package__': None, '__loader__': <type at remote 0x8deba8>, '__spec__': None, '__annotations__': {}, '__builtins__': <module at remote 0x7ffff75f9c28>, 'ldb': <module at remote 0x7ffff743a098>, 'a': <ldb.Ldb at remote 0x7ffff7584950>}, flags=0x7fffffffb2ec) at ../Python/pythonrun.c:959 #14 0x00000000006319bd in PyRun_SimpleStringFlags (command=0x7ffff7517ae0 "from samba import ldb;a = ldb.Ldb(); ldb.Dn(a, '<>').canonical_str()\n", flags=0x7fffffffb2ec) at ../Python/pythonrun.c:455 #15 0x0000000000654378 in pymain_run_command (cf=0x7fffffffb2ec, command=<optimized out>) at ../Modules/main.c:385 #16 pymain_run_python (pymain=0x7fffffffb3c0) at ../Modules/main.c:2871 #17 pymain_main (pymain=<optimized out>, pymain=<optimized out>) at ../Modules/main.c:3038 #18 0x00000000006544ae in _Py_UnixMain (argc=<optimized out>, argv=<optimized out>) at ../Modules/main.c:3073 #19 0x00007ffff7d8fb6b in __libc_start_main (main=0x4bc950 <main>, argc=3, argv=0x7fffffffb508, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7fffffffb4f8) at ../csu/libc-start.c:308 #20 0x00000000005e04ea in _start () at ../Modules/main.c:725
Created attachment 15323 [details] proof of concept crasher The attached file reduces it down to the essential calls (no Python). after configure and make, $ ./bin/ldb_dn_crash Segmentation fault (core dumped) Results are the same on master, 4.10, 4.8, and 4.5. ldb_dn_canonical_string() is not the only function that will trigger this -- ldb_dn_explode() is setting comp_num to 1.
It seems likely that an unauthenticated user entity *somehow* cause the server to examine and manipulate arbitrary DN strings, though we haven't yet been able to trigger this over LDAP.
Created attachment 15327 [details] a patch that stops the crash This patch is probably sufficient. I have old patches sitting around that aim to decomplify ldb_dn_explode(), which I think might also be useful in master.
Created attachment 15328 [details] a patch that stops the crash (v2)
(In reply to Douglas Bagnall from comment #3) > I have old patches sitting around that aim to decomplify ldb_dn_explode(), which I think might also be useful in master. That's this, which fixes the problem: http://git.catalyst.net.nz/gitweb?p=samba.git;a=commitdiff;h=228ea5cf47d0e3a814cf430a7c31f0b4a6ca3b08
Created attachment 15330 [details] a script to look for ldb_dn segfaults If the attached program does not crash, we are probably safe against any short string!
Created attachment 15378 [details] updated script to look for ldb_dn segfaults
Is the crash only viable via ldb_dn_canonical_string()? This (in Samba) is only accessible via trusted inputs, so we might have dodged this one.
(In reply to Andrew Bartlett from comment #8) > Is the crash only viable via ldb_dn_canonical_string()? Alas, no. Any function that iterates over the comonents crashes. There are a few of them.
(In reply to Douglas Bagnall from comment #9) So, how should we proceed from here? I can't get ldb_dn_get_casefold() to crash on your simple reproducer.
These are the routes I initially found: py_ldb_dn_canonical_ex_str py_ldb_dn_canonical_str py_ldb_dn_extended_str py_ldb_dn_get_casefold py_ldb_dn_methods py_ldb_dn_repr
(In reply to Douglas Bagnall from comment #11) I can't make ldb_dn_get_extended_linearized() crash. I'm thinking we escape this by the skin of our teeth, but proving a negative is hard.
So, here is the plan. Douglas and I have looked at this again and while this is a serious issue, it should be dealt with as a public bug as we can't find a path to an actual exploit over LDAP. The most likely issue, if there is one, is a crash in the LDAP server (a DoS) which is now mitigated by a auto-restart for the prefork workers in 4.10 and later. Samba 4.11 will need to ship with this fixed Samba 4.7 -> 4.10 use the forking LDAP server, making this a self-DoS for the default configuration (but an issue if -M single or -M prefork were specified). Previous investigations did not find other projects that allow untrusted input into LDB DN functions. To allow any last-moment objections I'll wait 24 hours to make this public. Then I'll propose one or other of the referenced patches to master and backport etc.
Removing embargo, we will just fix this as an important but otherwise quite ordinary bug.
(In reply to Douglas Bagnall from comment #5) this patch no longer applies bug the fix from attachment 15328 [details] fixes it. We will need a proper test, not just a PoC binary to merge this.
Created attachment 15403 [details] some ldb_dn explode tests OK. I do have these tests I was working on a while ago.
A MR with tests (which sadly fail) is here: https://gitlab.com/samba-team/samba/merge_requests/737 I'll have a go at making those pass.