From a9f21ddc5988b58bf3f152b3320991a72a017575 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Thu, 22 Feb 2018 12:49:36 +0000 Subject: [PATCH 1/4] s4/dsdb: python3 api should take 'bytes' Attributes are properly represented by 'bytes' and *maybe* can be converted into strings (if they are text). py_dsdb_normalise_attributes currently expects strings, this is fine in python2 however in python3 we need to actually pass a 'bytes' class. Signed-off-by: Noel Power Signed-off-by: Douglas Bagnall --- source4/dsdb/pydsdb.c | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/source4/dsdb/pydsdb.c b/source4/dsdb/pydsdb.c index d38d7095efa..8d84a16dd18 100644 --- a/source4/dsdb/pydsdb.c +++ b/source4/dsdb/pydsdb.c @@ -625,7 +625,6 @@ static PyObject *py_dsdb_normalise_attributes(PyObject *self, PyObject *args) TALLOC_CTX *tmp_ctx; WERROR werr; Py_ssize_t i; - Py_ssize_t _size; PyTypeObject *py_type = NULL; PyObject *module = NULL; @@ -688,13 +687,16 @@ static PyObject *py_dsdb_normalise_attributes(PyObject *self, PyObject *args) for (i = 0; i < el->num_values; i++) { PyObject *item = PyList_GetItem(el_list, i); - if (!PyStr_Check(item)) { - PyErr_Format(PyExc_TypeError, "ldif_elements should be strings"); + if (!PyBytes_Check(item)) { + PyErr_Format(PyExc_TypeError, + "ldif_element type should be " + PY_DESC_PY3_BYTES + ); talloc_free(tmp_ctx); return NULL; } - el->values[i].data = (uint8_t *)PyStr_AsUTF8AndSize(item, &_size);; - el->values[i].length = _size; + el->values[i].data = (uint8_t *)PyBytes_AsString(item); + el->values[i].length = PyBytes_Size(item); } } From 1ba4df56290656201f4b1e5c24b80d3a70e46141 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 7 Mar 2018 14:39:54 +0000 Subject: [PATCH 2/4] python: Add compatability helpers to determine if type is really bytes py3compat has PyBytes_Check macro which evalates to PyString_Check in python2. To help switch behaviour based on whether you are dealing with the bytes type the following macros have been added. IsPy3Bytes IsPy3BytesOrString IsPy3Bytes will evaluate to false in python2 and will return the expected result in python3. IsPy3BytesOrString will test for string type alone in python2 or bytes and string in python3. Signed-off-by: Noel Power --- python/py3compat.h | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/python/py3compat.h b/python/py3compat.h index ce317c65f8e..5fa57f323d5 100644 --- a/python/py3compat.h +++ b/python/py3compat.h @@ -82,6 +82,19 @@ #define PY_DESC_PY3_BYTES "bytes" #define PY_DESC_PY3_STRING "string" +/* Determine if object is really bytes, for code that runs + * in python2 & python3 (note: PyBytes_Check is replaced by + * PyString_Check in python2) so care needs to be taken when + * writing code that will check if incoming type is bytes that + * will work as expected in python2 & python3 + */ + +#define IsPy3Bytes PyBytes_Check + +#define IsPy3BytesOrString(pystr) \ + (PyStr_Check(pystr) || PyBytes_Check(pystr)) + + /* Ints */ #define PyInt_Type PyLong_Type @@ -152,6 +165,17 @@ #define PY_DESC_PY3_BYTES "string" #define PY_DESC_PY3_STRING "unicode" +/* Determine if object is really bytes, for code that runs + * in python2 & python3 (note: PyBytes_Check is replaced by + * PyString_Check in python2) so care needs to be taken when + * writing code that will check if incoming type is bytes that + * will work as expected in python2 & python3 + */ + +#define IsPy3Bytes(pystr) false + +#define IsPy3BytesOrString PyStr_Check + /* PyArg_ParseTuple/Py_BuildValue argument */ #define PYARG_BYTES_LEN "s#" From 1e346d3f84ae93c01bf8295d922eca190b11943b Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 28 Feb 2018 16:25:55 +1300 Subject: [PATCH 3/4] s4/librpc: GUID should accept string or bytes in python3 In python3 you can't store a binary blob GUID in a string class, you need to use 'bytes'. This change ensures python2 code continues to use a string and in python3 both 'bytes' and 'string' are supported. Signed-off-by: Noel Power --- source4/librpc/ndr/py_misc.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/source4/librpc/ndr/py_misc.c b/source4/librpc/ndr/py_misc.c index 6316b9b6704..849a11460a4 100644 --- a/source4/librpc/ndr/py_misc.c +++ b/source4/librpc/ndr/py_misc.c @@ -97,11 +97,19 @@ static int py_GUID_init(PyObject *self, PyObject *args, PyObject *kwargs) DATA_BLOB guid_val; Py_ssize_t _size; - if (!PyStr_Check(str)) { - PyErr_SetString(PyExc_TypeError, "Expected a string argument to GUID()"); + if (!IsPy3BytesOrString(str)) { + PyErr_SetString(PyExc_TypeError, "Expected a string or bytes argument to GUID()"); return -1; } - guid_val.data = (uint8_t *)PyStr_AsUTF8AndSize(str, &_size); + + if (!IsPy3Bytes(str)) { + guid_val.data = + (uint8_t *)PyStr_AsUTF8AndSize(str, + &_size); + } else { + guid_val.data = (uint8_t *)PyBytes_AsString(str); + _size = PyBytes_Size(str); + } guid_val.length = _size; status = GUID_from_data_blob(&guid_val, guid); if (!NT_STATUS_IS_OK(status)) { From 9bf2668568f93111e3dc2abc1ca0812cc1342378 Mon Sep 17 00:00:00 2001 From: Noel Power Date: Wed, 28 Feb 2018 14:40:26 +0000 Subject: [PATCH 4/4] samba python tests: Ensure GUIDTests cover all input formats Signed-off-by: Noel Power --- python/samba/tests/dcerpc/misc.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/python/samba/tests/dcerpc/misc.py b/python/samba/tests/dcerpc/misc.py index 2ec095063e9..a66f6c25bd0 100644 --- a/python/samba/tests/dcerpc/misc.py +++ b/python/samba/tests/dcerpc/misc.py @@ -23,6 +23,9 @@ text1 = "76f53846-a7c2-476a-ae2c-20e2b80d7b34" text2 = "344edffa-330a-4b39-b96e-2c34da52e8b1" +text3_s = "00112233-4455-6677-8899-aabbccddeeff" +text3_b = b"\x33\x22\x11\x00\x55\x44\x77\x66\x88\x99\xaa\xbb\xcc\xdd\xee\xff" +text3_h = "33221100554477668899aabbccddeeff" if PY3: @@ -55,6 +58,18 @@ def test_compare_same(self): self.assertEquals(guid1, guid2) self.assertEquals(0, cmp(guid1, guid2)) + def test_binary_format(self): + guid = misc.GUID(text3_b) + self.assertEquals(text3_s, str(guid)) + + def test_strhex_format(self): + guid = misc.GUID(text3_h) + self.assertEquals(text3_s, str(guid)) + + def test_bracketed_format(self): + guid = misc.GUID('{'+ text3_s + '}') + self.assertEquals(text3_s, str(guid)) + class PolicyHandleTests(samba.tests.TestCase):