From 6fb9a67a46b4159fe59937f177243369b12a0e8f Mon Sep 17 00:00:00 2001 From: tron Date: Wed, 21 Aug 2013 11:47:14 +0000 Subject: Pullup ticket #4213 - requested by spz lang/python27: security patch Revisions pulled up: - lang/python27/Makefile 1.27 - lang/python27/PLIST.common 1.6 - lang/python27/distinfo 1.25 - lang/python27/patches/patch-Lib_test_nullbytecert.pem 1.1 - lang/python27/patches/patch-Lib_test_test__ssl.py 1.1 - lang/python27/patches/patch-Misc_NEWS 1.1 - lang/python27/patches/patch-Modules___ssl.c 1.2 --- Module Name: pkgsrc Committed By: spz Date: Sun Aug 18 13:42:14 UTC 2013 Modified Files: pkgsrc/lang/python27: Makefile PLIST.common distinfo pkgsrc/lang/python27/patches: patch-Modules___ssl.c Added Files: pkgsrc/lang/python27/patches: patch-Lib_test_nullbytecert.pem patch-Lib_test_test__ssl.py patch-Misc_NEWS Log Message: patch for CVE-2013-4238 taken from http://hg.python.org/cpython/rev/bd2360476bdb --- lang/python27/Makefile | 3 +- lang/python27/PLIST.common | 3 +- lang/python27/distinfo | 7 +- .../patches/patch-Lib_test_nullbytecert.pem | 98 ++++++++++++++++++++++ lang/python27/patches/patch-Lib_test_test__ssl.py | 67 +++++++++++++++ lang/python27/patches/patch-Misc_NEWS | 20 +++++ lang/python27/patches/patch-Modules___ssl.c | 98 +++++++++++++++++++++- 7 files changed, 291 insertions(+), 5 deletions(-) create mode 100644 lang/python27/patches/patch-Lib_test_nullbytecert.pem create mode 100644 lang/python27/patches/patch-Lib_test_test__ssl.py create mode 100644 lang/python27/patches/patch-Misc_NEWS diff --git a/lang/python27/Makefile b/lang/python27/Makefile index 328cb215808..5db58717980 100644 --- a/lang/python27/Makefile +++ b/lang/python27/Makefile @@ -1,8 +1,9 @@ -# $NetBSD: Makefile,v 1.25 2013/04/17 10:00:57 sbd Exp $ +# $NetBSD: Makefile,v 1.25.2.1 2013/08/21 11:47:14 tron Exp $ .include "dist.mk" PKGNAME= python27-${PY_DISTVERSION} +PKGREVISION= 1 CATEGORIES= lang python MAINTAINER= pkgsrc-users@NetBSD.org diff --git a/lang/python27/PLIST.common b/lang/python27/PLIST.common index 39de0d015a2..330b903691e 100644 --- a/lang/python27/PLIST.common +++ b/lang/python27/PLIST.common @@ -1,4 +1,4 @@ -@comment $NetBSD: PLIST.common,v 1.5 2013/05/27 16:24:00 adam Exp $ +@comment $NetBSD: PLIST.common,v 1.5.2.1 2013/08/21 11:47:14 tron Exp $ bin/2to3-${PY_VER_SUFFIX} bin/pydoc${PY_VER_SUFFIX} bin/python${PY_VER_SUFFIX} @@ -2815,6 +2815,7 @@ lib/python${PY_VER_SUFFIX}/test/mp_fork_bomb.py lib/python${PY_VER_SUFFIX}/test/mp_fork_bomb.pyc lib/python${PY_VER_SUFFIX}/test/mp_fork_bomb.pyo lib/python${PY_VER_SUFFIX}/test/nokia.pem +lib/python${PY_VER_SUFFIX}/test/nullbytecert.pem lib/python${PY_VER_SUFFIX}/test/nullcert.pem lib/python${PY_VER_SUFFIX}/test/outstanding_bugs.py lib/python${PY_VER_SUFFIX}/test/outstanding_bugs.pyc diff --git a/lang/python27/distinfo b/lang/python27/distinfo index ae13e206da3..b5003f5a2a8 100644 --- a/lang/python27/distinfo +++ b/lang/python27/distinfo @@ -1,4 +1,4 @@ -$NetBSD: distinfo,v 1.24 2013/05/27 16:24:00 adam Exp $ +$NetBSD: distinfo,v 1.24.2.1 2013/08/21 11:47:14 tron Exp $ SHA1 (Python-2.7.5.tar.xz) = b7389791f789625c2ba9d897aa324008ff482daf RMD160 (Python-2.7.5.tar.xz) = baa7ad7d82cb4dc0a70b6a7aded43b7b78b0067e @@ -6,7 +6,10 @@ Size (Python-2.7.5.tar.xz) = 10252148 bytes SHA1 (patch-Include_node.h) = 673d148b625711ac47e4bfeb0f5b0d5b31f94d7e SHA1 (patch-Include_pyerrors.h) = 3eba043c83b1d1df4918524f7b53047a6ed372ae SHA1 (patch-Lib_distutils_unixccompiler.py) = 3964e0c6bd2b3899b24b31892d5ad830ae04d308 -SHA1 (patch-Modules___ssl.c) = a635ffc52becf2f78c4f1ebae17587cfdf3dffc6 +SHA1 (patch-Lib_test_nullbytecert.pem) = 22f866c0e00016a0931985f672513c0d533d9db5 +SHA1 (patch-Lib_test_test__ssl.py) = 30622803b45db6c081f8a507139ad0ad56b1ba43 +SHA1 (patch-Misc_NEWS) = a7871e744f509aca0e3eac7bc3c71d742621ca20 +SHA1 (patch-Modules___ssl.c) = d66e56a1a88d885564cbbce7afef55467f80d19a SHA1 (patch-Modules_getpath.c) = f68b38eb90f974b67ceab3922ce7f92eb77f25c3 SHA1 (patch-aa) = 990e4025bb6a37715e1f5df1831499f0ab08acfa SHA1 (patch-ab) = 0d0ae9802dfe3b85659adb16793affd8c4ffce43 diff --git a/lang/python27/patches/patch-Lib_test_nullbytecert.pem b/lang/python27/patches/patch-Lib_test_nullbytecert.pem new file mode 100644 index 00000000000..9277d47ba69 --- /dev/null +++ b/lang/python27/patches/patch-Lib_test_nullbytecert.pem @@ -0,0 +1,98 @@ +$NetBSD: patch-Lib_test_nullbytecert.pem,v 1.1.2.2 2013/08/21 11:47:14 tron Exp $ + +patch for CVE-2013-4238 taken from +http://hg.python.org/cpython/rev/bd2360476bdb + +--- Lib/test/nullbytecert.pem.orig 2013-08-18 11:36:17.000000000 +0000 ++++ Lib/test/nullbytecert.pem +@@ -0,0 +1,90 @@ ++Certificate: ++ Data: ++ Version: 3 (0x2) ++ Serial Number: 0 (0x0) ++ Signature Algorithm: sha1WithRSAEncryption ++ Issuer: C=US, ST=Oregon, L=Beaverton, O=Python Software Foundation, OU=Python Core Development, CN=null.python.org\x00example.org/emailAddress=python-dev@python.org ++ Validity ++ Not Before: Aug 7 13:11:52 2013 GMT ++ Not After : Aug 7 13:12:52 2013 GMT ++ Subject: C=US, ST=Oregon, L=Beaverton, O=Python Software Foundation, OU=Python Core Development, CN=null.python.org\x00example.org/emailAddress=python-dev@python.org ++ Subject Public Key Info: ++ Public Key Algorithm: rsaEncryption ++ Public-Key: (2048 bit) ++ Modulus: ++ 00:b5:ea:ed:c9:fb:46:7d:6f:3b:76:80:dd:3a:f3: ++ 03:94:0b:a7:a6:db:ec:1d:df:ff:23:74:08:9d:97: ++ 16:3f:a3:a4:7b:3e:1b:0e:96:59:25:03:a7:26:e2: ++ 88:a9:cf:79:cd:f7:04:56:b0:ab:79:32:6e:59:c1: ++ 32:30:54:eb:58:a8:cb:91:f0:42:a5:64:27:cb:d4: ++ 56:31:88:52:ad:cf:bd:7f:f0:06:64:1f:cc:27:b8: ++ a3:8b:8c:f3:d8:29:1f:25:0b:f5:46:06:1b:ca:02: ++ 45:ad:7b:76:0a:9c:bf:bb:b9:ae:0d:16:ab:60:75: ++ ae:06:3e:9c:7c:31:dc:92:2f:29:1a:e0:4b:0c:91: ++ 90:6c:e9:37:c5:90:d7:2a:d7:97:15:a3:80:8f:5d: ++ 7b:49:8f:54:30:d4:97:2c:1c:5b:37:b5:ab:69:30: ++ 68:43:d3:33:78:4b:02:60:f5:3c:44:80:a1:8f:e7: ++ f0:0f:d1:5e:87:9e:46:cf:62:fc:f9:bf:0c:65:12: ++ f1:93:c8:35:79:3f:c8:ec:ec:47:f5:ef:be:44:d5: ++ ae:82:1e:2d:9a:9f:98:5a:67:65:e1:74:70:7c:cb: ++ d3:c2:ce:0e:45:49:27:dc:e3:2d:d4:fb:48:0e:2f: ++ 9e:77:b8:14:46:c0:c4:36:ca:02:ae:6a:91:8c:da: ++ 2f:85 ++ Exponent: 65537 (0x10001) ++ X509v3 extensions: ++ X509v3 Basic Constraints: critical ++ CA:FALSE ++ X509v3 Subject Key Identifier: ++ 88:5A:55:C0:52:FF:61:CD:52:A3:35:0F:EA:5A:9C:24:38:22:F7:5C ++ X509v3 Key Usage: ++ Digital Signature, Non Repudiation, Key Encipherment ++ X509v3 Subject Alternative Name: ++ ************************************************************* ++ WARNING: The values for DNS, email and URI are WRONG. OpenSSL ++ doesn't print the text after a NULL byte. ++ ************************************************************* ++ DNS:altnull.python.org, email:null@python.org, URI:http://null.python.org, IP Address:192.0.2.1, IP Address:2001:DB8:0:0:0:0:0:1 ++ Signature Algorithm: sha1WithRSAEncryption ++ ac:4f:45:ef:7d:49:a8:21:70:8e:88:59:3e:d4:36:42:70:f5: ++ a3:bd:8b:d7:a8:d0:58:f6:31:4a:b1:a4:a6:dd:6f:d9:e8:44: ++ 3c:b6:0a:71:d6:7f:b1:08:61:9d:60:ce:75:cf:77:0c:d2:37: ++ 86:02:8d:5e:5d:f9:0f:71:b4:16:a8:c1:3d:23:1c:f1:11:b3: ++ 56:6e:ca:d0:8d:34:94:e6:87:2a:99:f2:ae:ae:cc:c2:e8:86: ++ de:08:a8:7f:c5:05:fa:6f:81:a7:82:e6:d0:53:9d:34:f4:ac: ++ 3e:40:fe:89:57:7a:29:a4:91:7e:0b:c6:51:31:e5:10:2f:a4: ++ 60:76:cd:95:51:1a:be:8b:a1:b0:fd:ad:52:bd:d7:1b:87:60: ++ d2:31:c7:17:c4:18:4f:2d:08:25:a3:a7:4f:b7:92:ca:e2:f5: ++ 25:f1:54:75:81:9d:b3:3d:61:a2:f7:da:ed:e1:c6:6f:2c:60: ++ 1f:d8:6f:c5:92:05:ab:c9:09:62:49:a9:14:ad:55:11:cc:d6: ++ 4a:19:94:99:97:37:1d:81:5f:8b:cf:a3:a8:96:44:51:08:3d: ++ 0b:05:65:12:eb:b6:70:80:88:48:72:4f:c6:c2:da:cf:cd:8e: ++ 5b:ba:97:2f:60:b4:96:56:49:5e:3a:43:76:63:04:be:2a:f6: ++ c1:ca:a9:94 ++-----BEGIN CERTIFICATE----- ++MIIE2DCCA8CgAwIBAgIBADANBgkqhkiG9w0BAQUFADCBxTELMAkGA1UEBhMCVVMx ++DzANBgNVBAgMBk9yZWdvbjESMBAGA1UEBwwJQmVhdmVydG9uMSMwIQYDVQQKDBpQ ++eXRob24gU29mdHdhcmUgRm91bmRhdGlvbjEgMB4GA1UECwwXUHl0aG9uIENvcmUg ++RGV2ZWxvcG1lbnQxJDAiBgNVBAMMG251bGwucHl0aG9uLm9yZwBleGFtcGxlLm9y ++ZzEkMCIGCSqGSIb3DQEJARYVcHl0aG9uLWRldkBweXRob24ub3JnMB4XDTEzMDgw ++NzEzMTE1MloXDTEzMDgwNzEzMTI1MlowgcUxCzAJBgNVBAYTAlVTMQ8wDQYDVQQI ++DAZPcmVnb24xEjAQBgNVBAcMCUJlYXZlcnRvbjEjMCEGA1UECgwaUHl0aG9uIFNv ++ZnR3YXJlIEZvdW5kYXRpb24xIDAeBgNVBAsMF1B5dGhvbiBDb3JlIERldmVsb3Bt ++ZW50MSQwIgYDVQQDDBtudWxsLnB5dGhvbi5vcmcAZXhhbXBsZS5vcmcxJDAiBgkq ++hkiG9w0BCQEWFXB5dGhvbi1kZXZAcHl0aG9uLm9yZzCCASIwDQYJKoZIhvcNAQEB ++BQADggEPADCCAQoCggEBALXq7cn7Rn1vO3aA3TrzA5QLp6bb7B3f/yN0CJ2XFj+j ++pHs+Gw6WWSUDpybiiKnPec33BFawq3kyblnBMjBU61ioy5HwQqVkJ8vUVjGIUq3P ++vX/wBmQfzCe4o4uM89gpHyUL9UYGG8oCRa17dgqcv7u5rg0Wq2B1rgY+nHwx3JIv ++KRrgSwyRkGzpN8WQ1yrXlxWjgI9de0mPVDDUlywcWze1q2kwaEPTM3hLAmD1PESA ++oY/n8A/RXoeeRs9i/Pm/DGUS8ZPINXk/yOzsR/XvvkTVroIeLZqfmFpnZeF0cHzL ++08LODkVJJ9zjLdT7SA4vnne4FEbAxDbKAq5qkYzaL4UCAwEAAaOB0DCBzTAMBgNV ++HRMBAf8EAjAAMB0GA1UdDgQWBBSIWlXAUv9hzVKjNQ/qWpwkOCL3XDALBgNVHQ8E ++BAMCBeAwgZAGA1UdEQSBiDCBhYIeYWx0bnVsbC5weXRob24ub3JnAGV4YW1wbGUu ++Y29tgSBudWxsQHB5dGhvbi5vcmcAdXNlckBleGFtcGxlLm9yZ4YpaHR0cDovL251 ++bGwucHl0aG9uLm9yZwBodHRwOi8vZXhhbXBsZS5vcmeHBMAAAgGHECABDbgAAAAA ++AAAAAAAAAAEwDQYJKoZIhvcNAQEFBQADggEBAKxPRe99SaghcI6IWT7UNkJw9aO9 ++i9eo0Fj2MUqxpKbdb9noRDy2CnHWf7EIYZ1gznXPdwzSN4YCjV5d+Q9xtBaowT0j ++HPERs1ZuytCNNJTmhyqZ8q6uzMLoht4IqH/FBfpvgaeC5tBTnTT0rD5A/olXeimk ++kX4LxlEx5RAvpGB2zZVRGr6LobD9rVK91xuHYNIxxxfEGE8tCCWjp0+3ksri9SXx ++VHWBnbM9YaL32u3hxm8sYB/Yb8WSBavJCWJJqRStVRHM1koZlJmXNx2BX4vPo6iW ++RFEIPQsFZRLrtnCAiEhyT8bC2s/Njlu6ly9gtJZWSV46Q3ZjBL4q9sHKqZQ= ++-----END CERTIFICATE----- diff --git a/lang/python27/patches/patch-Lib_test_test__ssl.py b/lang/python27/patches/patch-Lib_test_test__ssl.py new file mode 100644 index 00000000000..c951c5163ca --- /dev/null +++ b/lang/python27/patches/patch-Lib_test_test__ssl.py @@ -0,0 +1,67 @@ +$NetBSD: patch-Lib_test_test__ssl.py,v 1.1.2.2 2013/08/21 11:47:14 tron Exp $ + +patch for CVE-2013-4238 taken from +http://hg.python.org/cpython/rev/bd2360476bdb + +--- Lib/test/test_ssl.py.orig 2013-05-12 03:32:47.000000000 +0000 ++++ Lib/test/test_ssl.py +@@ -25,6 +25,7 @@ ssl = test_support.import_module("ssl") + HOST = test_support.HOST + CERTFILE = None + SVN_PYTHON_ORG_ROOT_CERT = None ++NULLBYTECERT = None + + def handle_error(prefix): + exc_format = ' '.join(traceback.format_exception(*sys.exc_info())) +@@ -123,6 +124,27 @@ class BasicSocketTests(unittest.TestCase + ('DNS', 'projects.forum.nokia.com')) + ) + ++ def test_parse_cert_CVE_2013_4238(self): ++ p = ssl._ssl._test_decode_cert(NULLBYTECERT) ++ if test_support.verbose: ++ sys.stdout.write("\n" + pprint.pformat(p) + "\n") ++ subject = ((('countryName', 'US'),), ++ (('stateOrProvinceName', 'Oregon'),), ++ (('localityName', 'Beaverton'),), ++ (('organizationName', 'Python Software Foundation'),), ++ (('organizationalUnitName', 'Python Core Development'),), ++ (('commonName', 'null.python.org\x00example.org'),), ++ (('emailAddress', 'python-dev@python.org'),)) ++ self.assertEqual(p['subject'], subject) ++ self.assertEqual(p['issuer'], subject) ++ self.assertEqual(p['subjectAltName'], ++ (('DNS', 'altnull.python.org\x00example.com'), ++ ('email', 'null@python.org\x00user@example.org'), ++ ('URI', 'http://null.python.org\x00http://example.org'), ++ ('IP Address', '192.0.2.1'), ++ ('IP Address', '2001:DB8:0:0:0:0:0:1\n')) ++ ) ++ + def test_DER_to_PEM(self): + with open(SVN_PYTHON_ORG_ROOT_CERT, 'r') as f: + pem = f.read() +@@ -1360,7 +1382,7 @@ else: + + + def test_main(verbose=False): +- global CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, NOKIACERT ++ global CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, NOKIACERT, NULLBYTECERT + CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, + "keycert.pem") + SVN_PYTHON_ORG_ROOT_CERT = os.path.join( +@@ -1368,10 +1390,13 @@ def test_main(verbose=False): + "https_svn_python_org_root.pem") + NOKIACERT = os.path.join(os.path.dirname(__file__) or os.curdir, + "nokia.pem") ++ NULLBYTECERT = os.path.join(os.path.dirname(__file__) or os.curdir, ++ "nullbytecert.pem") + + if (not os.path.exists(CERTFILE) or + not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT) or +- not os.path.exists(NOKIACERT)): ++ not os.path.exists(NOKIACERT) or ++ not os.path.exists(NULLBYTECERT)): + raise test_support.TestFailed("Can't read certificate files!") + + tests = [BasicTests, BasicSocketTests] diff --git a/lang/python27/patches/patch-Misc_NEWS b/lang/python27/patches/patch-Misc_NEWS new file mode 100644 index 00000000000..17a05723b70 --- /dev/null +++ b/lang/python27/patches/patch-Misc_NEWS @@ -0,0 +1,20 @@ +$NetBSD: patch-Misc_NEWS,v 1.1.2.2 2013/08/21 11:47:14 tron Exp $ + +patch for CVE-2013-4238 taken from +http://hg.python.org/cpython/rev/bd2360476bdb + +--- Misc/NEWS.orig 2013-05-12 03:32:49.000000000 +0000 ++++ Misc/NEWS +@@ -26,6 +26,12 @@ Core and Builtins + Library + ------- + ++- Issue #18709: Fix CVE-2013-4238. The SSL module now handles NULL bytes ++ inside subjectAltName correctly. Formerly the module has used OpenSSL's ++ GENERAL_NAME_print() function to get the string represention of ASN.1 ++ strings for ``rfc822Name`` (email), ``dNSName`` (DNS) and ++ ``uniformResourceIdentifier`` (URI). ++ + - Issue #16601: Restarting iteration over tarfile no more continues from where + it left off. Patch by Michael Birtwell. + diff --git a/lang/python27/patches/patch-Modules___ssl.c b/lang/python27/patches/patch-Modules___ssl.c index b0f92d7e34c..ecc2fbf5e0f 100644 --- a/lang/python27/patches/patch-Modules___ssl.c +++ b/lang/python27/patches/patch-Modules___ssl.c @@ -1,7 +1,10 @@ -$NetBSD: patch-Modules___ssl.c,v 1.1 2013/04/17 13:21:05 obache Exp $ +$NetBSD: patch-Modules___ssl.c,v 1.1.2.1 2013/08/21 11:47:14 tron Exp $ * from cygport 2.6.2-ssl-threads.patch +patch for CVE-2013-4238 taken from +http://hg.python.org/cpython/rev/bd2360476bdb + --- Modules/_ssl.c.orig 2013-04-06 14:02:36.000000000 +0000 +++ Modules/_ssl.c @@ -16,6 +16,10 @@ @@ -15,3 +18,96 @@ $NetBSD: patch-Modules___ssl.c,v 1.1 2013/04/17 13:21:05 obache Exp $ #ifdef WITH_THREAD #include "pythread.h" #define PySSL_BEGIN_ALLOW_THREADS { \ +@@ -738,13 +742,16 @@ _get_peer_alt_names (X509 *certificate) + ext->value->length)); + + for(j = 0; j < sk_GENERAL_NAME_num(names); j++) { +- + /* get a rendering of each name in the set of names */ ++ int gntype; ++ ASN1_STRING *as = NULL; + + name = sk_GENERAL_NAME_value(names, j); +- if (name->type == GEN_DIRNAME) { +- +- /* we special-case DirName as a tuple of tuples of attributes */ ++ gntype = name-> type; ++ switch (gntype) { ++ case GEN_DIRNAME: ++ /* we special-case DirName as a tuple of ++ tuples of attributes */ + + t = PyTuple_New(2); + if (t == NULL) { +@@ -764,11 +771,61 @@ _get_peer_alt_names (X509 *certificate) + goto fail; + } + PyTuple_SET_ITEM(t, 1, v); ++ break; + +- } else { ++ case GEN_EMAIL: ++ case GEN_DNS: ++ case GEN_URI: ++ /* GENERAL_NAME_print() doesn't handle NULL bytes in ASN1_string ++ correctly, CVE-2013-4238 */ ++ t = PyTuple_New(2); ++ if (t == NULL) ++ goto fail; ++ switch (gntype) { ++ case GEN_EMAIL: ++ v = PyString_FromString("email"); ++ as = name->d.rfc822Name; ++ break; ++ case GEN_DNS: ++ v = PyString_FromString("DNS"); ++ as = name->d.dNSName; ++ break; ++ case GEN_URI: ++ v = PyString_FromString("URI"); ++ as = name->d.uniformResourceIdentifier; ++ break; ++ } ++ if (v == NULL) { ++ Py_DECREF(t); ++ goto fail; ++ } ++ PyTuple_SET_ITEM(t, 0, v); ++ v = PyString_FromStringAndSize((char *)ASN1_STRING_data(as), ++ ASN1_STRING_length(as)); ++ if (v == NULL) { ++ Py_DECREF(t); ++ goto fail; ++ } ++ PyTuple_SET_ITEM(t, 1, v); ++ break; + ++ default: + /* for everything else, we use the OpenSSL print form */ +- ++ switch (gntype) { ++ /* check for new general name type */ ++ case GEN_OTHERNAME: ++ case GEN_X400: ++ case GEN_EDIPARTY: ++ case GEN_IPADD: ++ case GEN_RID: ++ break; ++ default: ++ if (PyErr_Warn(PyExc_RuntimeWarning, ++ "Unknown general name type") == -1) { ++ goto fail; ++ } ++ break; ++ } + (void) BIO_reset(biobuf); + GENERAL_NAME_print(biobuf, name); + len = BIO_gets(biobuf, buf, sizeof(buf)-1); +@@ -794,6 +851,7 @@ _get_peer_alt_names (X509 *certificate) + goto fail; + } + PyTuple_SET_ITEM(t, 1, v); ++ break; + } + + /* and add that rendering to the list */ -- cgit v1.2.3