Univention Bugzilla – Bug 44602
Login impossible after using huge password
Last modified: 2021-06-23 07:29:11 CEST
When trying to login with a valid username and a password that is >=2MiB no one will be able to login afterwards. Can be fixed by doing: univention-management-console-server restart This bug was discovered by Elena Maier.
In which test case is this happening?
This is not happening in any test case, but someone with bad intentions could block the UMC login for everyone.
We are using the crypt algorithm from libc (http://man7.org/linux/man-pages/man3/crypt.3.html) with sha512. In that case the password isn't truncated and the time to hash the password depends on its length. A ldap bind has the same problem. The problem starts getting noticeable at around 1E5 characters. The password does not need to be valid.
strace of univention-management-console-server shows: futex(0xee7ea0, FUTEX_WAIT_PRIVATE, 0, NULL
Still so with UCS-4.3-2
b232518621 Bug #44602: restrict password length to 10_000 for pam authentication ba1f6abd78 Bug #44602: debian changelog 5af883ea44 Bug #44602: yaml b436164f0c Revert "Bug #44602: restrict password length to 10_000 for pam authentication" d26c330ca4 Bug #44602: debian changelog ---------- Successful build Package: univention-management-console Version: 11.0.4-20A~4.4.0.201906212025 8411fa5c09 Bug #44602: restrict password length to 1000 for non saml authentication ee35cfc20b Bug #44602: Debian changelog 2a3e5115a1 Bug #44602: yaml
Created attachment 10082 [details] Exploit dos.py
The fix is not effective: One can easily woraround by specifying "auth_type: Anything, e.g. None" in the request, so that the length is not checked. The correct check would be: body.get('auth_type') != 'SAML'
I think we should investigate the real underlying issue: Why is the UMC-Server hanging? The request is executed in a thread, so nothing should block. But it hangs in a futex. Maybe our thread handling is broken?
Thread 2 (Thread 0x7f9b6e1bd700 (LWP 21819)): #0 sha512_process_block (buffer=<optimized out>, len=len@entry=49792, ctx=ctx@entry=0x7f9b6e1bb5b0) at ./sha512-block.c:70 #1 0x00007f9b8a4e3dbb in __sha512_process_bytes (buffer=<optimized out>, buffer@entry=0x7f9b680cd920, len=<optimized out>, len@entry=50010, ctx=ctx@entry=0x7f9b6e1bb5b0) at sha512.c:215 #2 0x00007f9b8a4e08c2 in __sha512_crypt_r (key=<optimized out>, key@entry=0x7f9b680cd920 "univention", 'A' <repeats 190 times>..., salt=0x7f9b6e1bb328 "p3/yYoizYnBYiz1B\\u5\217\233\177", salt@entry=0x7f9b680c69a0 "$6$p3/yYoizYnBYiz1B$TnZ5ULjUxy8hU8AotZpXsavCY474omncOUCc7SIqN9jUZ9E64jLClRZTYkexzuoEdQRGyYZP2kTEQ/iHME9I41", buffer=<optimized out>, buflen=<optimized out>, buflen@entry=131232) at sha512-crypt.c:240 #3 0x00007f9b8a4deed8 in __crypt_r (key=key@entry=0x7f9b680cd920 "univention", 'A' <repeats 190 times>..., salt=<optimized out>, salt@entry=0x7f9b680c69a0 "$6$p3/yYoizYnBYiz1B$TnZ5ULjUxy8hU8AotZpXsavCY474omncOUCc7SIqN9jUZ9E64jLClRZTYkexzuoEdQRGyYZP2kTEQ/iHME9I41", data=data@entry=0x7f9b680f46c0) at crypt-entry.c:102 #4 0x00007f9b6f84dbee in verify_pwd_hash (p=p@entry=0x7f9b680cd920 "univention", 'A' <repeats 190 times>..., hash=<optimized out>, nullok=0) at passverify.c:111 #5 0x00007f9b6f84d21f in _unix_verify_password (pamh=pamh@entry=0x5608cb1968c0, name=0x7f9b680ad7c0 "Administrator", p=0x7f9b680cd920 "univention", 'A' <repeats 190 times>..., ctrl=ctrl@entry=8389156) at support.c:801 #6 0x00007f9b6f84a595 in pam_sm_authenticate (pamh=0x5608cb1968c0, flags=<optimized out>, argc=<optimized out>, argv=<optimized out>) at pam_unix_auth.c:180 #7 0x00007f9b7a33c0d7 in ?? () from /lib/x86_64-linux-gnu/libpam.so.0 #8 0x00007f9b7a33b84d in pam_authenticate () from /lib/x86_64-linux-gnu/libpam.so.0 #9 0x00007f9b734af5b5 in ?? () from /usr/lib/python2.7/dist-packages/PAM.x86_64-linux-gnu.so #10 0x00005608ca1f184a in call_function (oparg=<optimized out>, pp_stack=0x7f9b6e1bba48) at ../Python/ceval.c:4352 #11 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #12 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #13 0x00005608ca20c778 in function_call.lto_priv () at ../Objects/funcobject.c:523 #14 0x00005608ca1de0c3 in PyObject_Call () at ../Objects/abstract.c:2547 #15 0x00005608ca1f83a0 in ext_do_call (nk=0, na=3, flags=<optimized out>, pp_stack=0x7f9b6e1bbd00, func=<function at remote 0x7f9b736c3488>) at ../Python/ceval.c:4666 #16 PyEval_EvalFrameEx () at ../Python/ceval.c:3028 #17 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #18 0x00005608ca20c778 in function_call.lto_priv () at ../Objects/funcobject.c:523 #19 0x00005608ca1de0c3 in PyObject_Call () at ../Objects/abstract.c:2547 #20 0x00005608ca1f83a0 in ext_do_call (nk=0, na=1, flags=<optimized out>, pp_stack=0x7f9b6e1bbfb0, func=<function at remote 0x7f9b736c3aa0>) at ../Python/ceval.c:4666 #21 PyEval_EvalFrameEx () at ../Python/ceval.c:3028 #22 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #23 0x00005608ca20c5be in function_call.lto_priv () at ../Objects/funcobject.c:523 #24 0x00005608ca1de0c3 in PyObject_Call () at ../Objects/abstract.c:2547 #25 0x00005608ca222e1e in instancemethod_call.lto_priv () at ../Objects/classobject.c:2602 #26 0x00005608ca1de0c3 in PyObject_Call () at ../Objects/abstract.c:2547 #27 0x00005608ca2d1718 in instance_call.lto_priv () at ../Objects/classobject.c:2153 #28 0x00005608ca1de0c3 in PyObject_Call () at ../Objects/abstract.c:2547 #29 0x00005608ca1f735f in do_call (nk=<optimized out>, na=0, pp_stack=0x7f9b6e1bc538, func=<Callback(_args=(), _function=<instancemethod at remote 0x7f9b7326c5f0>, _kwargs={u'username': u'Administrator', 'new_password': None, u'method': u'POST', u'headers': {u'X-Requested-With': u'XMLHttpRequest', u'Accept-Language': u'de-DE'}, u'password': u'univentionAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA...(truncated)) at ../Python/ceval.c:4569 #30 call_function (oparg=<optimized out>, pp_stack=0x7f9b6e1bc538) at ../Python/ceval.c:4374 #31 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #32 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #33 0x00005608ca20c778 in function_call.lto_priv () at ../Objects/funcobject.c:523 #34 0x00005608ca1de0c3 in PyObject_Call () at ../Objects/abstract.c:2547 #35 0x00005608ca1f83a0 in ext_do_call (nk=0, na=1, flags=<optimized out>, pp_stack=0x7f9b6e1bc7f0, func=<function at remote 0x7f9b8edc65f0>) at ../Python/ceval.c:4666 #36 PyEval_EvalFrameEx () at ../Python/ceval.c:3028 #37 0x00005608ca1f714f in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7f9b6e1bc938, func=<function at remote 0x7f9b916b3b18>) at ../Python/ceval.c:4437 #38 call_function (oparg=<optimized out>, pp_stack=0x7f9b6e1bc938) at ../Python/ceval.c:4372 #39 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #40 0x00005608ca1f714f in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7f9b6e1bca88, func=<function at remote 0x7f9b916b3c80>) at ../Python/ceval.c:4437 #41 call_function (oparg=<optimized out>, pp_stack=0x7f9b6e1bca88) at ../Python/ceval.c:4372 #42 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #43 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #44 0x00005608ca20c5be in function_call.lto_priv () at ../Objects/funcobject.c:523 #45 0x00005608ca1de0c3 in PyObject_Call () at ../Objects/abstract.c:2547 #46 0x00005608ca222e1e in instancemethod_call.lto_priv () at ../Objects/classobject.c:2602 #47 0x00005608ca1de0c3 in PyObject_Call () at ../Objects/abstract.c:2547 #48 0x00005608ca1fba10 in PyEval_CallObjectWithKeywords () at ../Python/ceval.c:4221 #49 0x00005608ca2c4722 in t_bootstrap () at ../Modules/threadmodule.c:620 #50 0x00007f9b928ec4a4 in start_thread (arg=0x7f9b6e1bd700) at pthread_create.c:456 #51 0x00007f9b91d09d0f in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:97 Thread 1 (Thread 0x7f9b92d135c0 (LWP 21455)): #0 0x00007f9b928f4556 in futex_abstimed_wait_cancelable (private=0, abstime=0x0, expected=0, futex_word=0x5608cc950dc0) at ../sysdeps/unix/sysv/linux/futex-internal.h:205 #1 do_futex_wait (sem=sem@entry=0x5608cc950dc0, abstime=0x0) at sem_waitcommon.c:111 #2 0x00007f9b928f4604 in __new_sem_wait_slow (sem=0x5608cc950dc0, abstime=0x0) at sem_waitcommon.c:181 #3 0x00005608ca1cca04 in PyThread_acquire_lock () at ../Python/thread_pthread.h:324 #4 0x00005608ca21883a in PyEval_RestoreThread () at ../Python/ceval.c:359 #5 0x00005608ca2400ca in PyFile_WriteString () at ../Objects/fileobject.c:2637 #6 0x00005608ca1f567e in PyEval_EvalFrameEx () at ../Python/ceval.c:2034 #7 0x00005608ca1f714f in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffe9672ff38, func=<function at remote 0x7f9b736e7488>) at ../Python/ceval.c:4437 #8 call_function (oparg=<optimized out>, pp_stack=0x7ffe9672ff38) at ../Python/ceval.c:4372 #9 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #10 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #11 0x00005608ca1f7448 in fast_function (nk=0, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffe96730148, func=<function at remote 0x7f9b736eab90>) at ../Python/ceval.c:4447 #12 call_function (oparg=<optimized out>, pp_stack=0x7ffe96730148) at ../Python/ceval.c:4372 #13 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #14 0x00005608ca1f714f in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffe96730298, func=<function at remote 0x7f9b736eac08>) at ../Python/ceval.c:4437 #15 call_function (oparg=<optimized out>, pp_stack=0x7ffe96730298) at ../Python/ceval.c:4372 #16 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #17 0x00005608ca1f714f in fast_function (nk=<optimized out>, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffe967303e8, func=<function at remote 0x7f9b9049c1b8>) at ../Python/ceval.c:4437 #18 call_function (oparg=<optimized out>, pp_stack=0x7ffe967303e8) at ../Python/ceval.c:4372 #19 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #20 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #21 0x00005608ca1f7b78 in fast_function (nk=0, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffe967305f8, func=<function at remote 0x7f9b90c27488>) at ../Python/ceval.c:4447 #22 call_function (oparg=<optimized out>, pp_stack=0x7ffe967305f8) at ../Python/ceval.c:4372 #23 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #24 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #25 0x00005608ca1f7b78 in fast_function (nk=0, na=<optimized out>, n=<optimized out>, pp_stack=0x7ffe96730808, func=<function at remote 0x7f9b90c276e0>) at ../Python/ceval.c:4447 #26 call_function (oparg=<optimized out>, pp_stack=0x7ffe96730808) at ../Python/ceval.c:4372 #27 PyEval_EvalFrameEx () at ../Python/ceval.c:2989 #28 0x00005608ca1ef9f5 in PyEval_EvalCodeEx () at ../Python/ceval.c:3584 #29 0x00005608ca1ef7b9 in PyEval_EvalCode (co=<optimized out>, globals=<optimized out>, locals=<optimized out>) at ../Python/ceval.c:669 #30 0x00005608ca21fbff in run_mod.lto_priv () at ../Python/pythonrun.c:1376 #31 0x00005608ca21ab52 in PyRun_FileExFlags () at ../Python/pythonrun.c:1362 #32 0x00005608ca21a69e in PyRun_SimpleFileExFlags () at ../Python/pythonrun.c:948 #33 0x00005608ca1cb771 in Py_Main () at ../Modules/main.c:640 #34 0x00007f9b91c412e1 in __libc_start_main (main=0x5608ca1cb0a0 <main>, argc=6, argv=0x7ffe96730c48, init=<optimized out>, fini=<optimized out>, rtld_fini=<optimized out>, stack_end=0x7ffe96730c38) at ../csu/libc-start.c:291 #35 0x00005608ca1caf9a in _start ()
Created attachment 10083 [details] reproduce.py I rewrote the components using a simple tornado server to check if it's a bug in python-notifier's thread implementation (which doesn't release all aquired thread locks with try-finally blocks). But the result is the same, that the process hangs in futex_abstimed_wait_cancelable(). root@master100:~# curl http://localhost:6671/ GET root@master100:~# curl http://localhost:6671/ GET root@master100:~# curl http://localhost:6671/ -F username=Administrator -F password="$(python -c 'print "univention" + "A" * 100000')" Done root@master100:~# curl http://localhost:6671/ ... hanging ...
A solution would be to call pam in a subprocess instead of a thread, but this is 10 times slower! But we could do this only for passwords >= 1000 chars (aka SAML), if we don't find the real cause.
(In reply to Florian Best from comment #10) > Thread 2 (Thread 0x7f9b6e1bd700 (LWP 21819)): > #0 sha512_process_block (buffer=…, len=…, ctx=…) at ./sha512-block.c:70 > #1 0x00007f9b8a4e3dbb in __sha512_process_bytes (buffer=…, buffer@entry=…, len=…, len@entry=…, ctx=…) at sha512.c:215 > #2 0x00007f9b8a4e08c2 in __sha512_crypt_r (key=…>, key@entry=…, …, salt=…, salt@entry=…, buffer=…, buflen=…, buflen@entry=…) at sha512-crypt.c:240 > #3 0x00007f9b8a4deed8 in __crypt_r (key=key@entry=…, …, salt=…, salt@entry=…, data=…) at crypt-entry.c:102 > #4 0x00007f9b6f84dbee in verify_pwd_hash (p=…, …, hash=…, nullok=0) at passverify.c:111 > #5 0x00007f9b6f84d21f in _unix_verify_password (pamh=…, name=…, p=…, …, ctrl=…) at support.c:801 > #6 0x00007f9b6f84a595 in pam_sm_authenticate (pamh=…, flags=…, argc=…, argv=…) at pam_unix_auth.c:180 > #7 0x00007f9b7a33c0d7 in ?? () from /lib/x86_64-linux-gnu/libpam.so.0 > #8 0x00007f9b7a33b84d in pam_authenticate () from /lib/x86_64-linux-gnu/libpam.so.0 > #9 0x00007f9b734af5b5 in ?? () from /usr/lib/python2.7/dist-packages/PAM.x86_64-linux-gnu.so > #10 0x00005608ca1f184a in call_function (oparg=…, pp_stack=…) at ../Python/ceval.c:4352 ... > Thread 1 (Thread 0x7f9b92d135c0 (LWP 21455)): > #0 0x00007f9b928f4556 in futex_abstimed_wait_cancelable (private=…, abstime=…, expected=…, futex_word=…) at ../sysdeps/unix/sysv/linux/futex-internal.h:205 > #1 do_futex_wait (sem=…, abstime=…) at sem_waitcommon.c:111 > #2 0x00007f9b928f4604 in __new_sem_wait_slow (sem=…, abstime=…) at sem_waitcommon.c:181 > #3 0x00005608ca1cca04 in PyThread_acquire_lock () at ../Python/thread_pthread.h:324 ... <https://docs.python.org/2/c-api/init.html#thread-state-and-the-global-interpreter-lock>
Created attachment 10086 [details] python-pam <https://docs.python.org/2/c-api/init.html#thread-state-and-the-global-interpreter-lock> So, C code which runs in python must explicitly allow to be executed in parallel. The python-pam code doesn't do this. Attached is a patch which would do this. But it seems pam_unix and pam_krb5 are not ready for that as they are segfaulting then.
Created attachment 10087 [details] patch for pam_krb5
Created attachment 10088 [details] patch for pam_unix
Created attachment 10089 [details] patch for pam_ldap
I tested all 3 patches, which restrict the password length to 512 characters. pam_unix has this hardcoded limit documented in it's manpage already, but it's only effective for unix_chkpwd().
I created Bug #49740 for pam_krb5, Bug #49741 for pam_unix and Bug #49738 for slapd itself. We can use this bug to configure pam_ldap parameters: man pam_ldap says: timelimit <timelimit> Specifies the time limit (in seconds) to use when performing searches. A value of zero (0), which is the default, is to wait indefinitely for searches to be completed. bind_timelimit <timelimit> Specifies the time limit (in seconds) to use when connecting to the directory server. This is distinct from the time limit specified in timelimit and affects the initial server connection only. (Server connections are otherwise cached.) Only some LDAP client libraries have the underlying functionality necessary to support this option. The default bind timelimit is 30 seconds.
I added the new error message of pam_unix to the known error messages. Also type checking of the arguments of a AUTH request has been added. And the password form field is now marked as required, just for a better browser usability. univention-management-console (11.0.4-28) 00c089b9d06a | Bug #44602: enhance authentication errors univention-management-console.yaml 00c089b9d06a | Bug #44602: enhance authentication errors
This breaks the tests. raise HTTPError(request, response, self.hostname) (2019-07-23 02:53:19.303416) univention.lib.umc.HTTPError: 591 on master090.autotest090.local (command/services/query): {"status": 591, "message": "Interner Server-Fehler in \"services/query\".", "traceback": "Interner Server-Fehler in \"services/query\".\nRequest: services/query\n\nTraceback (most recent call last):\n File \"/usr/lib/pymodules/python2.7/univention/management/console/base.py\", line 260, in execute\n function.__func__(self, request, *args, **kwargs)\n File \"/usr/lib/pymodules/python2.7/univention/management/console/modules/decorators.py\", line 180, in _response\n request.options = sanitize_args('request.options', {'request.options': request.options})\nTypeError: sanitize_args() takes exactly 3 arguments (2 given)", "location": "https://master090.autotest090.local/univention/command"}
Yes, sorry, fixed that typo.
(In reply to Florian Best from comment #22) > Yes, sorry, fixed that typo. great, thanks
Reopen: Failed build of univention-management-console 2019-07-23 17:07
(In reply to Erik Damrose from comment #24) > Reopen: Failed build of univention-management-console 2019-07-23 17:07 worked now (problem was the u-d-l package) Package: univention-management-console Version: 11.0.4-29A~4.4.0.201907231728 Branch: ucs_4.4-0 Scope: errata4.4-1 User: fbotner Host: dimma.knut.univention.de updated yaml (9c6785af20a6c7bd0e2bb7c537f0763abd479245)
Reopen Commit: [4.4-1 00c089b9d0] Bug #44602: enhance authentication errors breaks umc-command (found by test 59_udm.21_request_new_license) The new_password parameter is set as "None" by umc-command but the new sanitizer enforces a string for new_password
(In reply to Jürn Brodersen from comment #26) > Reopen > > Commit: > [4.4-1 00c089b9d0] Bug #44602: enhance authentication errors > breaks umc-command (found by test 59_udm.21_request_new_license) > > The new_password parameter is set as "None" by umc-command but the new > sanitizer enforces a string for new_password Nice find! thank you!
Fixed in: univention-management-console (11.0.4-30) 0b50ae279d43 | Bug #44602: add allow_none for new_password
OK: bug split into own bugs for pam_unix, pam_krb5 and ldap OK: authentication error improvements OK: yaml (316bd040fc Bug #44602: yaml) -> verified
<http://errata.software-univention.de/ucs/4.4/203.html>