The branch, master has been updated via 60df2a09a43 selftest: move some more expected failures to expectedfail.d via bda4e1233a1 ldb: Add more segfault tests DN handling via 8ac18495ba2 pyldb: don't allow py_ldb_dn_copy() with the wrong pyldb via d38a9e93cf3 python:upgrade/upgradeprovision: use dn.copy to align ldbs via 345eb854c3b pyldb: add dn.copy() python method. via fa9a32139ff s4:samba_upgradeprovision: align DN ownership via ed6d151c1b7 pyldb: add Message.ldb accessor via 8b6df2d0bca pyldb: add Dn.ldb accessor via a95e6aa5667 pyldb: add PyErr_internal_LDB_DN_OR_RAISE via d712c8d2edd pyldb: normalise name of pyldb_Message_Check via 72ad126ab74 ldb:pyldb: reorder structs for possible type-punning via c39021a494f pyldb: py_ldb_msg_set_dn checks dn ldb equality via 61ba0cc17df pyldb: py_ldb_msg_elements uses PyErr_LDB_MESSAGE_OR_RAISE via 9cadc61cd4c pyldb: py_ldb_msg_items checks for more errors via 6a2e6139ad0 pldb: py_ldb_msg_items uses PyErr_LDB_MESSAGE_OR_RAISE via b5fcc55b5ec pyldb: py_ldb_msg_contains() checks ldb equality via acba42b126c pyldb: py_ldb_msg_keys() uses PyErr_LDB_MESSAGE_OR_RAISE via d05ae6872b1 pyldb: py_ldb_msg_richcmp() uses PyErr_LDB_MESSAGE_OR_RAISE() via bc45a258d2a pyldb: use PyErr_LDB_MESSAGE_OR_RAISE() in various functions via f0e665f4a9a pyldb: add PyErr_LDB_MESSAGE_OR_RAISE() macro via b81b2578ad1 pyldb: catch up with README.Coding for some `PyArg_ParseTuple`s via 13545ed1390 pyldb: py_ldb_dn_concat() uses PyErr_LDB_DN_OR_RAISE via 1bbca1e3b42 pyldb: py_ldb_dn_len checks dn and ldb validity via f8b92e52811 pyldb: make py_ldb_dn_add_base() a bit less leaky via b83ea997e75 pyldb: py_ldb_dn_add_base() uses PyErr_LDB_DN_OR_RAISE via 67a9e573b00 pyldb: make py_ldb_dn_add_child() a bit less leaky via 310624ead50 pyldb: py_ldb_dn_add_child() uses PyErr_LDB_DN_OR_RAISE via 1eeb0e36516 pyldb: py_ldb_dn_get_parent() uses PyErr_LDB_DN_OR_RAISE via 8830149ef96 pyldb: py_ldb_dn_richcmp() uses PyErr_LDB_DN_OR_RAISE via 982a87cedfc pyldb: py_ldb_dn_get_extended_component() uses PyErr_LDB_DN_OR_RAISE via 5154c8c996f pyldb: py_ldb_dn_extended_str() uses PyErr_LDB_DN_OR_RAISE() via 0ce3f355022 pyldb: py_ldb_dn_get_casefold() uses PyErr_LDB_DN_OR_RAISE() via 85ba5d2c8f2 pyldb: py_ldb_dn_get_extended_component uses PyErr_LDB_DN_OR_RAISE() via 087d43ac615 pyldb: adapt some simple dn methods to use LDB_DN_OR_RAISE() via f98035a2a31 ldb:pyldb: PyErr_LDB_DN_OR_RAISE makes more rigourous checks via 8bb6287c3ba pytest:segfault: some more ldb crashes via 0bf80c10ca5 samba-tool domain backup: Use new ldb.disconnect() method to force-close files during backup via 8612b3e38b3 ldb:pytests: test ldb.connect() works after .disconnect() via fdc3212275b pyldb: Add ldb.disconnect() method to ensure DB handles are closed via 784ee21616a pyldb: Include a reference to the Ldb in objects that use via ffbe623963a selftest: Add tests that demonstrate the issues with ldb use after free via 3ffc6c139b0 pytest:krb5/lockout: associate user DN with the ldb it is used with from dbba6c22a41 auth/credentials: Read managed_password.passwords.query_interval only after parsing
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 60df2a09a4394d2b494224ad3d33314079e73066 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 22 16:20:18 2024 +1300 selftest: move some more expected failures to expectedfail.d Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> Autobuild-User(master): Andrew Bartlett <abart...@samba.org> Autobuild-Date(master): Wed Apr 10 06:15:46 UTC 2024 on atb-devel-224 commit bda4e1233a145f11aa92b89a5658d94cd9252267 Author: Andrew Bartlett <abart...@samba.org> Date: Mon Mar 25 22:21:19 2024 +1300 ldb: Add more segfault tests DN handling - from_dict DN use-after-free - check for the same directly creating the ldb.Message Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 8ac18495ba238985a82dbe5a3c95c78c3c51f4b6 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Mon Mar 18 12:24:53 2024 +1300 pyldb: don't allow py_ldb_dn_copy() with the wrong pyldb Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit d38a9e93cf3444d7fe3939728673a637a03eb819 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sun Mar 17 18:07:44 2024 +1300 python:upgrade/upgradeprovision: use dn.copy to align ldbs We need to do this when the dn is on a message from another ldb. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 345eb854c3b03a3c8e0e19fba0edb9eafd055ab9 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sun Mar 17 17:56:09 2024 +1300 pyldb: add dn.copy() python method. Sometimes you want to use a Dn object from one LDB with another LDB, but this no longer works. One way to do it is: new_dn = ldb.Dn(samdb, str(old_dn)) but with this, you can just: new_dn = old_dn.copy(samdb) or, if you are putting it on a message which has a DN: msg.dn = old_dn.copy(msg.ldb) Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit fa9a32139fff4e8d49ee3f5331e6f8ce6fb47ae1 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sun Mar 17 14:44:32 2024 +1300 s4:samba_upgradeprovision: align DN ownership Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit ed6d151c1b73985e3cf81ea4561fab843dd62142 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sun Mar 17 14:25:18 2024 +1300 pyldb: add Message.ldb accessor See the last commit for comments about how this is useful for debugging. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 8b6df2d0bca12fc33b60c5702b4afe36597cc775 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sun Mar 17 14:24:03 2024 +1300 pyldb: add Dn.ldb accessor This, and the next commit, might help in debugging when you see a traceback that ends like this: File "/data/samba/samba/bin/samba_upgradeprovision", line 664, in add_missing_object delta.dn = dn RuntimeError: DN is from the wrong LDB in this case you could force a solution with something like: delta.dn = ldb.dn(delta.ldb, str(dn)) Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit a95e6aa56677dfcff1c15925aa8c042a1f3db9a1 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Wed Mar 13 17:28:57 2024 +1300 pyldb: add PyErr_internal_LDB_DN_OR_RAISE This might be faster than the circuitous route. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit d712c8d2edd168a45c558a7fe26459a41823e53d Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Wed Mar 13 17:28:07 2024 +1300 pyldb: normalise name of pyldb_Message_Check c.f. pyldb_MessageElement_Check, pyldb_Dn_Check. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 72ad126ab74b247460ccf4bdb590efc28cbd2be8 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 8 10:42:06 2024 +1300 ldb:pyldb: reorder structs for possible type-punning Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit c39021a494f81ea90e03d63e7f5d09098e9438e1 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 17:17:25 2024 +1300 pyldb: py_ldb_msg_set_dn checks dn ldb equality Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 61ba0cc17df6a0ecf59b61c7d6da3bb7c0bdeea3 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 17:16:34 2024 +1300 pyldb: py_ldb_msg_elements uses PyErr_LDB_MESSAGE_OR_RAISE Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 9cadc61cd4c5dd2632b120ce37969f25743e0fbc Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Sat Mar 16 11:15:31 2024 +1300 pyldb: py_ldb_msg_items checks for more errors Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 6a2e6139ad00de95eaf89ff44939f2d680f07f03 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 17:15:28 2024 +1300 pldb: py_ldb_msg_items uses PyErr_LDB_MESSAGE_OR_RAISE Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit b5fcc55b5ec0bae2782ca29c1e868712129cfe8d Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 17:09:53 2024 +1300 pyldb: py_ldb_msg_contains() checks ldb equality We can't use PyErr_LDB_MESSAGE_OR_RAISE() here, because the return type is int, not PyObject*. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit acba42b126cdc4b6c165a0075bc3986a72bfe5eb Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 13:50:40 2024 +1300 pyldb: py_ldb_msg_keys() uses PyErr_LDB_MESSAGE_OR_RAISE We change the [unused, because it always cast] signature of py_ldb_msg_iter() in the same commit, because that is just a wrapper around _keys() and this maintains bisectability with the least fuss. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit d05ae6872b17ec0df5343d38c5638064a676f6db Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 17:04:59 2024 +1300 pyldb: py_ldb_msg_richcmp() uses PyErr_LDB_MESSAGE_OR_RAISE() Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit bc45a258d2a4442ff7294cd6cdadd6f780a06cd4 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 13:08:03 2024 +1300 pyldb: use PyErr_LDB_MESSAGE_OR_RAISE() in various functions In these simple cases, we are: 1. replacing the first argument `PyObject *` with `PyLdbMessageObject *`. 2. adding a `struct ldb_message *msg = NULL;` variable. 3. `PyErr_LDB_MESSAGE_OR_RAISE(self, msg);`. 4. changing the `self->msg` to `msg`. 5. adding { } to the `if (!PyArg_ParseTuple() return NULL;`. 6. replacing `self->pyldb` with `pyldb_Message_get_pyldb(self)` Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit f0e665f4a9accad5b8814e27553010645b9feddb Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 16:41:43 2024 +1300 pyldb: add PyErr_LDB_MESSAGE_OR_RAISE() macro The Python level message has a reference to an LDB, which should be NULL, or the same as the dn's LDB, lest one of them is freed early. The message LDB will be NULL until a DN is set, and if the DN is replaced, the LDB is also be replaced (see py_ldb_msg_set_dn), so it is *unlikely* for these to get out of sync. In addition, fetching msg.dn via python compares the LDBs at that point (py_ldb_msg_get_dn). Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit b81b2578ad14992c388fc1b3a8055f5d8c0f2dee Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 17:32:51 2024 +1300 pyldb: catch up with README.Coding for some `PyArg_ParseTuple`s Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 13545ed139034d0ca9543445e3303c08620d5d57 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 17:27:58 2024 +1300 pyldb: py_ldb_dn_concat() uses PyErr_LDB_DN_OR_RAISE Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 1bbca1e3b42bb87b65b89129b42cf01dc4937345 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 17:26:38 2024 +1300 pyldb: py_ldb_dn_len checks dn and ldb validity Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit f8b92e52811a87193ed326a97ff8ac2f440dcf7b Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 12:38:00 2024 +1300 pyldb: make py_ldb_dn_add_base() a bit less leaky Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit b83ea997e759c494847c2f12b1fb667b07e22bc8 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 17:24:48 2024 +1300 pyldb: py_ldb_dn_add_base() uses PyErr_LDB_DN_OR_RAISE Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 67a9e573b009443990b426c3f136862c2b3a5705 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 12:11:18 2024 +1300 pyldb: make py_ldb_dn_add_child() a bit less leaky Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 310624ead5078d38bca7abaac35e06b49b129568 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Wed Apr 10 14:41:09 2024 +1200 pyldb: py_ldb_dn_add_child() uses PyErr_LDB_DN_OR_RAISE for self->dn only. The other dn is a different story, next commit. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 1eeb0e36516cbc52a4adc80c391d35afcf39de7c Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 17:21:34 2024 +1300 pyldb: py_ldb_dn_get_parent() uses PyErr_LDB_DN_OR_RAISE Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 8830149ef9648a6f1af33a120ac82930c5306404 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 17:19:21 2024 +1300 pyldb: py_ldb_dn_richcmp() uses PyErr_LDB_DN_OR_RAISE The `if (!pyldb_Dn_Check(pydn2))` might seem redundant, but we need it to return Py_NotImplemented before the _OR_RAISE macro raises TypeError. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 982a87cedfcf71dd22a89cfa36285a333e6dcec8 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 17:10:17 2024 +1300 pyldb: py_ldb_dn_get_extended_component() uses PyErr_LDB_DN_OR_RAISE Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 5154c8c996f16531743e40cb839de49ddc66b2c6 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 17:07:52 2024 +1300 pyldb: py_ldb_dn_extended_str() uses PyErr_LDB_DN_OR_RAISE() Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 0ce3f35502243847201e99fa7fa0312667d00e11 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Wed Apr 10 14:40:42 2024 +1200 pyldb: py_ldb_dn_get_casefold() uses PyErr_LDB_DN_OR_RAISE() Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> # Conflicts: # selftest/knownfail.d/ldb-use-after-free-segfault commit 85ba5d2c8f2708d284a973bb6b372faea542a393 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Fri Mar 15 11:00:50 2024 +1300 pyldb: py_ldb_dn_get_extended_component uses PyErr_LDB_DN_OR_RAISE() Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 087d43ac615e12665538cb0bc858113e797ebcba Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 16:38:14 2024 +1300 pyldb: adapt some simple dn methods to use LDB_DN_OR_RAISE() We treat self as PyObject, and only trust its DN once it has been laundered by PyErr_LDB_DN_OR_RAISE(). There are more of these to come in the next few commits, but these are the simplest ones (on a textual level -- the others are simple too, but look different). Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit f98035a2a3147f7d935a356d2a273e0bfd0796f2 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 15:10:07 2024 +1300 ldb:pyldb: PyErr_LDB_DN_OR_RAISE makes more rigourous checks This changes what happens all over the place (lib/ldb/pyldb.c, source4/dns_server/pydns.c, source4/dsdb/pydsdb.c), but causes no problems because it just checks what we always assumed. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 8bb6287c3ba20209c5d8352f3b2d90275561fb56 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 09:32:47 2024 +1300 pytest:segfault: some more ldb crashes Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit 0bf80c10ca50515fc64561db68d3c3283945252a Author: Andrew Bartlett <abart...@samba.org> Date: Wed Dec 6 12:38:54 2023 +1300 samba-tool domain backup: Use new ldb.disconnect() method to force-close files during backup Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 8612b3e38b3c64a6645c460ccdca52d059eeb75b Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Wed Mar 20 11:31:23 2024 +1300 ldb:pytests: test ldb.connect() works after .disconnect() Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> commit fdc3212275bc08f4a016e10923e689192ea7697f Author: Andrew Bartlett <abart...@samba.org> Date: Wed Dec 6 11:18:27 2023 +1300 pyldb: Add ldb.disconnect() method to ensure DB handles are closed This is vital in our backup code, which needs to actually close the LMDB at the correct point. The Python ldb object itself is left in more or less the same state as one that has not connected to a server or database (it is a very simple wrapper in itself), and can be reconnected using the .connect() method. Pair-programmed-with: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Signed-off-by: Andrew Bartlett <abart...@samba.org> Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 784ee21616a60993ecc0979f83fbb04467d57af9 Author: Andrew Bartlett <abart...@samba.org> Date: Wed Nov 8 10:43:38 2023 +1300 pyldb: Include a reference to the Ldb in objects that use This will help avoid use-after-free of the internally cached ldb within struct ldb_dn by ensuring that it lives as long. Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit ffbe623963a9b495e70f767512e1b147ebebba1f Author: Andrew Bartlett <abart...@samba.org> Date: Mon Dec 4 12:00:12 2023 +1300 selftest: Add tests that demonstrate the issues with ldb use after free Signed-off-by: Andrew Bartlett <abart...@samba.org> Reviewed-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> commit 3ffc6c139b03d51b0e30ed2e3a4d512ba5ca2dc1 Author: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Date: Thu Mar 14 13:25:48 2024 +1300 pytest:krb5/lockout: associate user DN with the ldb it is used with LDB is soon going to object strongly to Python DNs that don't come from the ldb that they are being used with, for memory safety reasons. Signed-off-by: Douglas Bagnall <douglas.bagn...@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abart...@samba.org> ----------------------------------------------------------------------- Summary of changes: lib/ldb/ABI/pyldb-util-2.9.0.sigs | 2 +- lib/ldb/pyldb.c | 701 +++++++++++++++++---- lib/ldb/pyldb.h | 80 ++- lib/ldb/pyldb_util.c | 5 +- lib/ldb/tests/python/api.py | 9 + python/samba/netcmd/domain/backup.py | 2 + python/samba/tests/krb5/lockout_tests.py | 13 +- python/samba/tests/segfault.py | 346 ++++++++++ python/samba/upgrade.py | 2 +- python/samba/upgradehelpers.py | 8 +- selftest/expectedfail.d/ldap-tlsverifypeer | 10 + selftest/knownfail | 10 - .../knownfail.d/pyldb-segfaults | 0 source4/dns_server/pydns.c | 2 +- source4/dsdb/pydsdb.c | 9 +- source4/scripting/bin/samba_upgradeprovision | 9 +- 16 files changed, 1034 insertions(+), 174 deletions(-) create mode 100644 selftest/expectedfail.d/ldap-tlsverifypeer copy buildtools/wafsamba/__init__.py => selftest/knownfail.d/pyldb-segfaults (100%) Changeset truncated at 500 lines: diff --git a/lib/ldb/ABI/pyldb-util-2.9.0.sigs b/lib/ldb/ABI/pyldb-util-2.9.0.sigs index 164a806b2ff..218d2161cd8 100644 --- a/lib/ldb/ABI/pyldb-util-2.9.0.sigs +++ b/lib/ldb/ABI/pyldb-util-2.9.0.sigs @@ -1,3 +1,3 @@ -pyldb_Dn_FromDn: PyObject *(struct ldb_dn *) +pyldb_Dn_FromDn: PyObject *(struct ldb_dn *, PyLdbObject *) pyldb_Object_AsDn: bool (TALLOC_CTX *, PyObject *, struct ldb_context *, struct ldb_dn **) pyldb_check_type: bool (PyObject *, const char *) diff --git a/lib/ldb/pyldb.c b/lib/ldb/pyldb.c index cd4268a9a74..f416bfe6d5d 100644 --- a/lib/ldb/pyldb.c +++ b/lib/ldb/pyldb.c @@ -58,14 +58,14 @@ struct py_ldb_search_iterator_reply { }; void initldb(void); -static PyObject *PyLdbMessage_FromMessage(struct ldb_message *msg); +static PyObject *PyLdbMessage_FromMessage(struct ldb_message *msg, PyLdbObject *pyldb); static PyObject *PyExc_LdbError; static PyTypeObject PyLdbControl; static PyTypeObject PyLdbResult; static PyTypeObject PyLdbSearchIterator; static PyTypeObject PyLdbMessage; -#define PyLdbMessage_Check(ob) PyObject_TypeCheck(ob, &PyLdbMessage) +#define pyldb_Message_Check(ob) PyObject_TypeCheck(ob, &PyLdbMessage) static PyTypeObject PyLdbDn; #define pyldb_Dn_Check(ob) PyObject_TypeCheck(ob, &PyLdbDn) static PyTypeObject PyLdb; @@ -332,7 +332,7 @@ static PyObject *PyLdbControl_FromControl(struct ldb_control *control) * @param result LDB result to convert * @return Python object with converted result (a list object) */ -static PyObject *PyLdbResult_FromResult(struct ldb_result *result) +static PyObject *PyLdbResult_FromResult(struct ldb_result *result, PyLdbObject *pyldb) { PyLdbResultObject *ret; PyObject *list, *controls, *referals; @@ -348,6 +348,9 @@ static PyObject *PyLdbResult_FromResult(struct ldb_result *result) return NULL; } + ret->pyldb = pyldb; + Py_INCREF(ret->pyldb); + list = PyList_New(result->count); if (list == NULL) { PyErr_NoMemory(); @@ -356,7 +359,7 @@ static PyObject *PyLdbResult_FromResult(struct ldb_result *result) } for (i = 0; i < result->count; i++) { - PyObject *pymessage = PyLdbMessage_FromMessage(result->msgs[i]); + PyObject *pymessage = PyLdbMessage_FromMessage(result->msgs[i], pyldb); if (pymessage == NULL) { Py_DECREF(ret); Py_DECREF(list); @@ -434,10 +437,36 @@ static PyObject *PyLdbResult_FromResult(struct ldb_result *result) return (PyObject *)ret; } -static PyObject *py_ldb_dn_validate(PyLdbDnObject *self, + +/* + * PyErr_interal_LDB_DN_OR_RAISE does exactly what + * PyErr__LDB_DN_OR_RAISE does, but rather than going through the + * Python layer to import the Dn object, it directly uses the the + * address of the PyTypeObject. This is faster, but can only be done + * in pyldb.c. + */ +#define PyErr_internal_LDB_DN_OR_RAISE(_py_obj, dn) do { \ + PyLdbDnObject *_py_dn = NULL; \ + if (_py_obj == NULL || !pyldb_Dn_Check(_py_obj)) { \ + PyErr_SetString(PyExc_TypeError, "ldb Dn object required"); \ + return NULL; \ + } \ + _py_dn = (PyLdbDnObject *)_py_obj; \ + dn = pyldb_Dn_AS_DN(_py_dn); \ + if (_py_dn->pyldb->ldb_ctx != ldb_dn_get_ldb_context(dn)) { \ + PyErr_SetString(PyExc_RuntimeError, \ + "Dn has a stale LDB connection"); \ + return NULL; \ + } \ +} while(0) + + +static PyObject *py_ldb_dn_validate(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return PyBool_FromLong(ldb_dn_validate(self->dn)); + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + return PyBool_FromLong(ldb_dn_validate(dn)); } static PyObject *py_ldb_dn_is_valid(PyLdbDnObject *self, @@ -452,16 +481,21 @@ static PyObject *py_ldb_dn_is_special(PyLdbDnObject *self, return PyBool_FromLong(ldb_dn_is_special(self->dn)); } -static PyObject *py_ldb_dn_is_null(PyLdbDnObject *self, +static PyObject *py_ldb_dn_is_null(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return PyBool_FromLong(ldb_dn_is_null(self->dn)); + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + return PyBool_FromLong(ldb_dn_is_null(dn)); } -static PyObject *py_ldb_dn_get_casefold(PyLdbDnObject *self, +static PyObject *py_ldb_dn_get_casefold(PyObject *self, PyObject *Py_UNUSED(ignored)) { - const char *s = ldb_dn_get_casefold(self->dn); + const char *s = NULL; + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + s = ldb_dn_get_casefold(dn); if (s == NULL) { PyErr_NoMemory(); return NULL; @@ -469,43 +503,55 @@ static PyObject *py_ldb_dn_get_casefold(PyLdbDnObject *self, return PyUnicode_FromString(s); } -static PyObject *py_ldb_dn_get_linearized(PyLdbDnObject *self, +static PyObject *py_ldb_dn_get_linearized(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return PyUnicode_FromString(ldb_dn_get_linearized(self->dn)); + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + return PyUnicode_FromString(ldb_dn_get_linearized(dn)); } -static PyObject *py_ldb_dn_canonical_str(PyLdbDnObject *self, +static PyObject *py_ldb_dn_canonical_str(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return PyUnicode_FromString(ldb_dn_canonical_string(self->dn, self->dn)); + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + return PyUnicode_FromString(ldb_dn_canonical_string(dn, dn)); } -static PyObject *py_ldb_dn_canonical_ex_str(PyLdbDnObject *self, +static PyObject *py_ldb_dn_canonical_ex_str(PyObject *self, PyObject *Py_UNUSED(ignored)) { - return PyUnicode_FromString(ldb_dn_canonical_ex_string(self->dn, self->dn)); + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + return PyUnicode_FromString(ldb_dn_canonical_ex_string(dn, dn)); } -static PyObject *py_ldb_dn_extended_str(PyLdbDnObject *self, PyObject *args, PyObject *kwargs) +static PyObject *py_ldb_dn_extended_str(PyObject *self, PyObject *args, PyObject *kwargs) { const char * const kwnames[] = { "mode", NULL }; int mode = 1; + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); if (!PyArg_ParseTupleAndKeywords(args, kwargs, "|i", discard_const_p(char *, kwnames), - &mode)) + &mode)) { return NULL; - return PyUnicode_FromString(ldb_dn_get_extended_linearized(self->dn, self->dn, mode)); + } + return PyUnicode_FromString(ldb_dn_get_extended_linearized(dn, dn, mode)); } -static PyObject *py_ldb_dn_get_extended_component(PyLdbDnObject *self, PyObject *args) +static PyObject *py_ldb_dn_get_extended_component(PyObject *self, PyObject *args) { char *name; - const struct ldb_val *val; + const struct ldb_val *val = NULL; + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); - if (!PyArg_ParseTuple(args, "s", &name)) + if (!PyArg_ParseTuple(args, "s", &name)) { return NULL; - val = ldb_dn_get_extended_component(self->dn, name); + } + val = ldb_dn_get_extended_component(dn, name); if (val == NULL) { Py_RETURN_NONE; } @@ -513,23 +559,25 @@ static PyObject *py_ldb_dn_get_extended_component(PyLdbDnObject *self, PyObject return PyBytes_FromStringAndSize((const char *)val->data, val->length); } -static PyObject *py_ldb_dn_set_extended_component(PyLdbDnObject *self, PyObject *args) +static PyObject *py_ldb_dn_set_extended_component(PyObject *self, PyObject *args) { char *name; int err; uint8_t *value = NULL; Py_ssize_t size = 0; + struct ldb_dn *dn = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); if (!PyArg_ParseTuple(args, "sz#", &name, (char **)&value, &size)) return NULL; if (value == NULL) { - err = ldb_dn_set_extended_component(self->dn, name, NULL); + err = ldb_dn_set_extended_component(dn, name, NULL); } else { struct ldb_val val; val.data = (uint8_t *)value; val.length = size; - err = ldb_dn_set_extended_component(self->dn, name, &val); + err = ldb_dn_set_extended_component(dn, name, &val); } if (err != LDB_SUCCESS) { @@ -567,25 +615,33 @@ static PyObject *py_ldb_dn_check_special(PyLdbDnObject *self, PyObject *args) return PyBool_FromLong(ldb_dn_check_special(self->dn, name)); } -static PyObject *py_ldb_dn_richcmp(PyObject *dn1, PyObject *dn2, int op) +static PyObject *py_ldb_dn_richcmp(PyObject *pydn1, PyObject *pydn2, int op) { int ret; - if (!pyldb_Dn_Check(dn2)) { + struct ldb_dn *dn1 = NULL; + struct ldb_dn *dn2 = NULL; + if (!pyldb_Dn_Check(pydn2)) { Py_INCREF(Py_NotImplemented); return Py_NotImplemented; } - ret = ldb_dn_compare(pyldb_Dn_AS_DN(dn1), pyldb_Dn_AS_DN(dn2)); + PyErr_internal_LDB_DN_OR_RAISE(pydn1, dn1); + PyErr_internal_LDB_DN_OR_RAISE(pydn2, dn2); + + ret = ldb_dn_compare(dn1, dn2); return richcmp(ret, op); } -static PyObject *py_ldb_dn_get_parent(PyLdbDnObject *self, +static PyObject *py_ldb_dn_get_parent(PyObject *self, PyObject *Py_UNUSED(ignored)) { - struct ldb_dn *dn = pyldb_Dn_AS_DN((PyObject *)self); + struct ldb_dn *dn = NULL; struct ldb_dn *parent; - PyLdbDnObject *py_ret; + PyLdbDnObject *py_ret = NULL; + PyLdbDnObject *dn_self = NULL; TALLOC_CTX *mem_ctx = NULL; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + if (ldb_dn_get_comp_num(dn) < 1) { Py_RETURN_NONE; } @@ -609,64 +665,180 @@ static PyObject *py_ldb_dn_get_parent(PyLdbDnObject *self, talloc_free(mem_ctx); return NULL; } + dn_self = (PyLdbDnObject *)self; + py_ret->mem_ctx = mem_ctx; py_ret->dn = parent; + py_ret->pyldb = dn_self->pyldb; + Py_INCREF(py_ret->pyldb); return (PyObject *)py_ret; } -static PyObject *py_ldb_dn_add_child(PyLdbDnObject *self, PyObject *args) +static PyObject *py_ldb_dn_add_child(PyObject *self, PyObject *args) { - PyObject *py_other; - struct ldb_dn *dn, *other; + PyObject *py_other = NULL; + struct ldb_dn *dn = NULL; + struct ldb_dn *other = NULL; + TALLOC_CTX *tmp_ctx = NULL; bool ok; - if (!PyArg_ParseTuple(args, "O", &py_other)) + + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + + if (!PyArg_ParseTuple(args, "O", &py_other)) { return NULL; + } - dn = pyldb_Dn_AS_DN((PyObject *)self); + /* + * pyldb_Object_AsDn only uses tmp_ctx if py_other is str/bytes, in + * which case it allocates a struct ldb_dn. If py_other is a PyLdbDn, + * tmp_ctx is unused and the underlying dn is borrowed. + * + * The pieces of other are reassembled onto dn using dn itself as a + * talloc context (ldb_dn_add_child assumes all dns are talloc + * contexts), after which we don't need any temporary DN we made. + */ + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } - if (!pyldb_Object_AsDn(NULL, py_other, ldb_dn_get_ldb_context(dn), &other)) + ok = pyldb_Object_AsDn(tmp_ctx, + py_other, + ldb_dn_get_ldb_context(dn), + &other); + if (!ok) { + TALLOC_FREE(tmp_ctx); return NULL; + } ok = ldb_dn_add_child(dn, other); + TALLOC_FREE(tmp_ctx); if (!ok) { PyErr_SetLdbError(PyExc_LdbError, LDB_ERR_OPERATIONS_ERROR, NULL); return NULL; } - Py_RETURN_TRUE; } -static PyObject *py_ldb_dn_add_base(PyLdbDnObject *self, PyObject *args) +static PyObject *py_ldb_dn_add_base(PyObject *self, PyObject *args) { - PyObject *py_other; - struct ldb_dn *other, *dn; + PyObject *py_other = NULL; + struct ldb_dn *other = NULL; + struct ldb_dn *dn = NULL; + TALLOC_CTX *tmp_ctx = NULL; bool ok; - if (!PyArg_ParseTuple(args, "O", &py_other)) - return NULL; - dn = pyldb_Dn_AS_DN((PyObject *)self); + PyErr_internal_LDB_DN_OR_RAISE(self, dn); - if (!pyldb_Object_AsDn(NULL, py_other, ldb_dn_get_ldb_context(dn), &other)) + if (!PyArg_ParseTuple(args, "O", &py_other)) { return NULL; + } + + /* + * As noted in py_ldb_dn_add_child() comments, if py_other is a + * string, other is an ephemeral struct ldb_dn, but if py_other is a + * python DN, other points to the corresponding long-lived DN. + */ + tmp_ctx = talloc_new(NULL); + if (tmp_ctx == NULL) { + PyErr_NoMemory(); + return NULL; + } + ok = pyldb_Object_AsDn(tmp_ctx, + py_other, + ldb_dn_get_ldb_context(dn), + &other); + if (!ok) { + TALLOC_FREE(tmp_ctx); + return NULL; + } ok = ldb_dn_add_base(dn, other); + TALLOC_FREE(tmp_ctx); if (!ok) { PyErr_SetLdbError(PyExc_LdbError, LDB_ERR_OPERATIONS_ERROR, NULL); return NULL; } - Py_RETURN_TRUE; } -static PyObject *py_ldb_dn_remove_base_components(PyLdbDnObject *self, PyObject *args) +static PyObject *py_ldb_dn_copy(struct ldb_dn *dn, PyLdbObject *pyldb); + +static PyObject *py_ldb_dn_copy_method(PyObject *self, PyObject *args) { - struct ldb_dn *dn; + struct ldb_dn *dn = NULL; + PyLdbObject *pyldb = NULL; + PyObject *obj = Py_None; + PyErr_internal_LDB_DN_OR_RAISE(self, dn); + + if (!PyArg_ParseTuple(args, "|O", &obj)) { + return NULL; + } + + if (obj == Py_None) { + /* + * With no argument, or None, dn.copy() uses its own ldb. + * + * There is not much reason to do this, other than as a + * convenience in this situation: + * + * >>> msg.dn = dn.copy(msg.ldb) + * + * when you don't know whether msg has a dn or not (if msg.ldb + * is None, msg will now belong to this dn's ldb). + */ + pyldb = ((PyLdbDnObject *)self)->pyldb; + } else if (PyObject_TypeCheck(obj, &PyLdb)) { + pyldb = (PyLdbObject *)obj; + } else { + PyErr_Format(PyExc_TypeError, + "Expected Ldb or None"); + return NULL; + } + if (pyldb != ((PyLdbDnObject *)self)->pyldb) { + /* + * This is unfortunate, but we can't make a copy of the dn directly, + * since the opaque struct ldb_dn has a pointer to the ldb it knows, + * and it is the WRONG ONE. + * + * Instead we go via string serialisation. + */ + char *dn_str = NULL; + struct ldb_dn *new_dn = NULL; + dn_str = ldb_dn_get_extended_linearized(pyldb->mem_ctx, dn, 1); + if (dn_str == NULL) { + PyErr_Format(PyExc_RuntimeError, + "Could not linearize DN"); + return NULL; + } + new_dn = ldb_dn_new(pyldb->mem_ctx, + pyldb->ldb_ctx, + dn_str); + + if (new_dn == NULL) { + PyErr_Format(PyExc_RuntimeError, + "Could not re-parse DN '%s'", + dn_str); + TALLOC_FREE(dn_str); + return NULL; + } + TALLOC_FREE(dn_str); + dn = new_dn; + } + return py_ldb_dn_copy(dn, pyldb); +} + +static PyObject *py_ldb_dn_remove_base_components(PyObject *self, PyObject *args) +{ + struct ldb_dn *dn = NULL; int i; bool ok; - if (!PyArg_ParseTuple(args, "i", &i)) + if (!PyArg_ParseTuple(args, "i", &i)) { return NULL; + } - dn = pyldb_Dn_AS_DN((PyObject *)self); + PyErr_internal_LDB_DN_OR_RAISE(self, dn); ok = ldb_dn_remove_base_components(dn, i); if (!ok) { @@ -677,14 +849,15 @@ static PyObject *py_ldb_dn_remove_base_components(PyLdbDnObject *self, PyObject Py_RETURN_TRUE; } -static PyObject *py_ldb_dn_is_child_of(PyLdbDnObject *self, PyObject *args) +static PyObject *py_ldb_dn_is_child_of(PyObject *self, PyObject *args) { PyObject *py_base; struct ldb_dn *dn, *base; - if (!PyArg_ParseTuple(args, "O", &py_base)) + if (!PyArg_ParseTuple(args, "O", &py_base)) { return NULL; + } - dn = pyldb_Dn_AS_DN((PyObject *)self); + PyErr_internal_LDB_DN_OR_RAISE(self, dn); if (!pyldb_Object_AsDn(NULL, py_base, ldb_dn_get_ldb_context(dn), &base)) return NULL; @@ -692,16 +865,17 @@ static PyObject *py_ldb_dn_is_child_of(PyLdbDnObject *self, PyObject *args) return PyBool_FromLong(ldb_dn_compare_base(base, dn) == 0); } -static PyObject *py_ldb_dn_get_component_name(PyLdbDnObject *self, PyObject *args) +static PyObject *py_ldb_dn_get_component_name(PyObject *self, PyObject *args) { - struct ldb_dn *dn; + struct ldb_dn *dn = NULL; const char *name; unsigned int num = 0; - if (!PyArg_ParseTuple(args, "I", &num)) + if (!PyArg_ParseTuple(args, "I", &num)) { return NULL; + } - dn = pyldb_Dn_AS_DN((PyObject *)self); + PyErr_internal_LDB_DN_OR_RAISE(self, dn); -- Samba Shared Repository