Blame SOURCES/pyOpenSSL-0.13.1-exception.patch

ec8eba
diff -up pyOpenSSL-0.13.1/OpenSSL/crypto/crypto.c.exception pyOpenSSL-0.13.1/OpenSSL/crypto/crypto.c
ec8eba
--- pyOpenSSL-0.13.1/OpenSSL/crypto/crypto.c.exception	2018-05-04 18:56:35.946486574 +0200
ec8eba
+++ pyOpenSSL-0.13.1/OpenSSL/crypto/crypto.c	2018-05-04 18:56:35.950486667 +0200
ec8eba
@@ -45,26 +45,72 @@ global_passphrase_callback(char *buf, in
ec8eba
 
ec8eba
     func = (PyObject *)cb_arg;
ec8eba
     argv = Py_BuildValue("(i)", rwflag);
ec8eba
-    if (argv == NULL)
ec8eba
+    if (argv == NULL) {
ec8eba
         return 0;
ec8eba
+    }
ec8eba
     ret = PyEval_CallObject(func, argv);
ec8eba
     Py_DECREF(argv);
ec8eba
-    if (ret == NULL)
ec8eba
+    if (ret == NULL) {
ec8eba
         return 0;
ec8eba
-    if (!PyBytes_Check(ret))
ec8eba
-    {
ec8eba
+    }
ec8eba
+    if (!PyBytes_Check(ret)) {
ec8eba
         Py_DECREF(ret);
ec8eba
         PyErr_SetString(PyExc_ValueError, "String expected");
ec8eba
         return 0;
ec8eba
     }
ec8eba
     nchars = PyBytes_Size(ret);
ec8eba
-    if (nchars > len)
ec8eba
-        nchars = len;
ec8eba
+    if (nchars > len) {
ec8eba
+       Py_DECREF(ret);
ec8eba
+       PyErr_SetString(PyExc_ValueError,
ec8eba
+                        "passphrase returned by callback is too long");
ec8eba
+       return 0;
ec8eba
+    }
ec8eba
     strncpy(buf, PyBytes_AsString(ret), nchars);
ec8eba
     Py_DECREF(ret);
ec8eba
     return nchars;
ec8eba
 }
ec8eba
 
ec8eba
+static PyObject *
ec8eba
+raise_current_error(void)
ec8eba
+{
ec8eba
+    if (PyErr_Occurred()) {
ec8eba
+        /*
ec8eba
+         * The python exception from callback is more informative than
ec8eba
+         * OpenSSL's error.
ec8eba
+         */
ec8eba
+        flush_error_queue();
ec8eba
+        return NULL;
ec8eba
+    }
ec8eba
+    exception_from_error_queue(crypto_Error);
ec8eba
+    return NULL;
ec8eba
+}
ec8eba
+
ec8eba
+static int
ec8eba
+setup_callback(int type, PyObject *pw, pem_password_cb **cb, void **cb_arg) {
ec8eba
+    if (pw == NULL) {
ec8eba
+        *cb = NULL;
ec8eba
+        *cb_arg = NULL;
ec8eba
+        return 1;
ec8eba
+    }
ec8eba
+    if (type != X509_FILETYPE_PEM) {
ec8eba
+        PyErr_SetString(PyExc_ValueError,
ec8eba
+                        "only FILETYPE_PEM key format supports encryption");
ec8eba
+        return 0;
ec8eba
+    }
ec8eba
+    if (PyBytes_Check(pw)) {
ec8eba
+        *cb = NULL;
ec8eba
+        *cb_arg = PyBytes_AsString(pw);
ec8eba
+    } else if (PyCallable_Check(pw)) {
ec8eba
+        *cb = global_passphrase_callback;
ec8eba
+        *cb_arg = pw;
ec8eba
+    } else {
ec8eba
+        PyErr_SetString(PyExc_TypeError,
ec8eba
+                        "Last argument must be string or callable");
ec8eba
+        return 0;
ec8eba
+    }
ec8eba
+    return 1;
ec8eba
+}
ec8eba
+
ec8eba
 static char crypto_load_privatekey_doc[] = "\n\
ec8eba
 Load a private key from a buffer\n\
ec8eba
 \n\
ec8eba
@@ -89,31 +135,20 @@ crypto_load_privatekey(PyObject *spam, P
ec8eba
     BIO *bio;
ec8eba
     EVP_PKEY *pkey;
ec8eba
 
ec8eba
-    if (!PyArg_ParseTuple(args, "is#|O:load_privatekey", &type, &buffer, &len, &pw))
ec8eba
+    if (!PyArg_ParseTuple(args, "is#|O:load_privatekey",
ec8eba
+                          &type, &buffer, &len, &pw)) {
ec8eba
+        return NULL;
ec8eba
+    }
ec8eba
+    if (!setup_callback(type, pw, &cb, &cb_arg)) {
ec8eba
         return NULL;
ec8eba
-
ec8eba
-    if (pw != NULL)
ec8eba
-    {
ec8eba
-        if (PyBytes_Check(pw))
ec8eba
-        {
ec8eba
-            cb = NULL;
ec8eba
-            cb_arg = PyBytes_AsString(pw);
ec8eba
-        }
ec8eba
-        else if (PyCallable_Check(pw))
ec8eba
-        {
ec8eba
-            cb = global_passphrase_callback;
ec8eba
-            cb_arg = pw;
ec8eba
-        }
ec8eba
-        else
ec8eba
-        {
ec8eba
-            PyErr_SetString(PyExc_TypeError, "Last argument must be string or callable");
ec8eba
-            return NULL;
ec8eba
-        }
ec8eba
     }
ec8eba
 
ec8eba
     bio = BIO_new_mem_buf(buffer, len);
ec8eba
-    switch (type)
ec8eba
-    {
ec8eba
+    if (bio == NULL) {
ec8eba
+        exception_from_error_queue(crypto_Error);
ec8eba
+        return NULL;
ec8eba
+    }
ec8eba
+    switch (type) {
ec8eba
         case X509_FILETYPE_PEM:
ec8eba
             pkey = PEM_read_bio_PrivateKey(bio, NULL, cb, cb_arg);
ec8eba
             break;
ec8eba
@@ -129,10 +164,8 @@ crypto_load_privatekey(PyObject *spam, P
ec8eba
     }
ec8eba
     BIO_free(bio);
ec8eba
 
ec8eba
-    if (pkey == NULL)
ec8eba
-    {
ec8eba
-        exception_from_error_queue(crypto_Error);
ec8eba
-        return NULL;
ec8eba
+    if (pkey == NULL) {
ec8eba
+        return raise_current_error();
ec8eba
     }
ec8eba
 
ec8eba
     return (PyObject *)crypto_PKey_New(pkey, 1);
ec8eba
@@ -168,49 +201,32 @@ crypto_dump_privatekey(PyObject *spam, P
ec8eba
     crypto_PKeyObj *pkey;
ec8eba
 
ec8eba
     if (!PyArg_ParseTuple(args, "iO!|sO:dump_privatekey", &type,
ec8eba
-			  &crypto_PKey_Type, &pkey, &cipher_name, &pw))
ec8eba
+                          &crypto_PKey_Type, &pkey, &cipher_name, &pw)) {
ec8eba
         return NULL;
ec8eba
-
ec8eba
-    if (cipher_name != NULL && pw == NULL)
ec8eba
-    {
ec8eba
+    }
ec8eba
+    if (cipher_name != NULL && pw == NULL) {
ec8eba
         PyErr_SetString(PyExc_ValueError, "Illegal number of arguments");
ec8eba
         return NULL;
ec8eba
     }
ec8eba
-    if (cipher_name != NULL)
ec8eba
-    {
ec8eba
+    if (cipher_name != NULL) {
ec8eba
         cipher = EVP_get_cipherbyname(cipher_name);
ec8eba
-        if (cipher == NULL)
ec8eba
-        {
ec8eba
+        if (cipher == NULL) {
ec8eba
             PyErr_SetString(PyExc_ValueError, "Invalid cipher name");
ec8eba
             return NULL;
ec8eba
         }
ec8eba
-        if (PyBytes_Check(pw))
ec8eba
-        {
ec8eba
-            cb = NULL;
ec8eba
-            cb_arg = PyBytes_AsString(pw);
ec8eba
-        }
ec8eba
-        else if (PyCallable_Check(pw))
ec8eba
-        {
ec8eba
-            cb = global_passphrase_callback;
ec8eba
-            cb_arg = pw;
ec8eba
-        }
ec8eba
-        else
ec8eba
-        {
ec8eba
-            PyErr_SetString(PyExc_TypeError, "Last argument must be string or callable");
ec8eba
+        if (!setup_callback(type, pw, &cb, &cb_arg)) {
ec8eba
             return NULL;
ec8eba
         }
ec8eba
     }
ec8eba
 
ec8eba
     bio = BIO_new(BIO_s_mem());
ec8eba
-    switch (type)
ec8eba
-    {
ec8eba
+    if (bio == NULL) {
ec8eba
+        exception_from_error_queue(crypto_Error);
ec8eba
+        return NULL;
ec8eba
+    }
ec8eba
+    switch (type) {
ec8eba
         case X509_FILETYPE_PEM:
ec8eba
             ret = PEM_write_bio_PrivateKey(bio, pkey->pkey, cipher, NULL, 0, cb, cb_arg);
ec8eba
-            if (PyErr_Occurred())
ec8eba
-            {
ec8eba
-                BIO_free(bio);
ec8eba
-                return NULL;
ec8eba
-            }
ec8eba
             break;
ec8eba
 
ec8eba
         case X509_FILETYPE_ASN1:
ec8eba
@@ -219,8 +235,12 @@ crypto_dump_privatekey(PyObject *spam, P
ec8eba
 
ec8eba
         case X509_FILETYPE_TEXT:
ec8eba
             rsa = EVP_PKEY_get1_RSA(pkey->pkey);
ec8eba
+            if (rsa == NULL) {
ec8eba
+                ret = 0;
ec8eba
+                break;
ec8eba
+            }
ec8eba
             ret = RSA_print(bio, rsa, 0);
ec8eba
-            RSA_free(rsa); 
ec8eba
+            RSA_free(rsa);
ec8eba
             break;
ec8eba
 
ec8eba
         default:
ec8eba
@@ -229,11 +249,9 @@ crypto_dump_privatekey(PyObject *spam, P
ec8eba
             return NULL;
ec8eba
     }
ec8eba
 
ec8eba
-    if (ret == 0)
ec8eba
-    {
ec8eba
+    if (ret == 0) {
ec8eba
         BIO_free(bio);
ec8eba
-        exception_from_error_queue(crypto_Error);
ec8eba
-        return NULL;
ec8eba
+        return raise_current_error();
ec8eba
     }
ec8eba
 
ec8eba
     buf_len = BIO_get_mem_data(bio, &temp);
ec8eba
@@ -513,8 +531,8 @@ crypto_load_pkcs7_data(PyObject *spam, P
ec8eba
     if (!PyArg_ParseTuple(args, "is#:load_pkcs7_data", &type, &buffer, &len))
ec8eba
         return NULL;
ec8eba
 
ec8eba
-    /* 
ec8eba
-     * Try to read the pkcs7 data from the bio 
ec8eba
+    /*
ec8eba
+     * Try to read the pkcs7 data from the bio
ec8eba
      */
ec8eba
     bio = BIO_new_mem_buf(buffer, len);
ec8eba
     switch (type)
ec8eba
diff -up pyOpenSSL-0.13.1/OpenSSL/test/test_crypto.py.exception pyOpenSSL-0.13.1/OpenSSL/test/test_crypto.py
ec8eba
--- pyOpenSSL-0.13.1/OpenSSL/test/test_crypto.py.exception	2018-05-04 18:56:35.948486620 +0200
ec8eba
+++ pyOpenSSL-0.13.1/OpenSSL/test/test_crypto.py	2018-05-04 18:57:16.363420609 +0200
ec8eba
@@ -7,7 +7,7 @@ Unit tests for L{OpenSSL.crypto}.
ec8eba
 
ec8eba
 from unittest import main
ec8eba
 
ec8eba
-import os, re
ec8eba
+import os, re, sys
ec8eba
 from subprocess import PIPE, Popen
ec8eba
 from datetime import datetime, timedelta
ec8eba
 
ec8eba
@@ -2038,6 +2038,18 @@ class FunctionTests(TestCase):
ec8eba
             load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, b("quack"))
ec8eba
 
ec8eba
 
ec8eba
+    def test_load_privatekey_passphraseWrongType(self):
ec8eba
+        """
ec8eba
+        :py:obj:`load_privatekey` raises :py:obj:`ValueError` when it is passed a passphrase
ec8eba
+        with a private key encoded in a format, that doesn't support
ec8eba
+        encryption.
ec8eba
+        """
ec8eba
+        key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
ec8eba
+        blob = dump_privatekey(FILETYPE_ASN1, key)
ec8eba
+        self.assertRaises(ValueError,
ec8eba
+            load_privatekey, FILETYPE_ASN1, blob, "secret")
ec8eba
+
ec8eba
+
ec8eba
     def test_load_privatekey_passphrase(self):
ec8eba
         """
ec8eba
         L{load_privatekey} can create a L{PKey} object from an encrypted PEM
ec8eba
@@ -2058,7 +2070,7 @@ class FunctionTests(TestCase):
ec8eba
         called = []
ec8eba
         def cb(*a):
ec8eba
             called.append(None)
ec8eba
-            return "quack"
ec8eba
+            return b("quack")
ec8eba
         self.assertRaises(
ec8eba
             Error,
ec8eba
             load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
ec8eba
@@ -2083,25 +2095,36 @@ class FunctionTests(TestCase):
ec8eba
     def test_load_privatekey_passphrase_exception(self):
ec8eba
         """
ec8eba
         An exception raised by the passphrase callback passed to
ec8eba
-        L{load_privatekey} causes L{OpenSSL.crypto.Error} to be raised.
ec8eba
-
ec8eba
-        This isn't as nice as just letting the exception pass through.  The
ec8eba
-        behavior might be changed to that eventually.
ec8eba
+        L{load_privatekey} is propagated.
ec8eba
         """
ec8eba
         def broken(ignored):
ec8eba
             raise RuntimeError("This is not working.")
ec8eba
         self.assertRaises(
ec8eba
-            Error,
ec8eba
+            RuntimeError,
ec8eba
             load_privatekey,
ec8eba
             FILETYPE_PEM, encryptedPrivateKeyPEM, broken)
ec8eba
 
ec8eba
 
ec8eba
+    def test_load_privatekey_passphrase_wrong_return_type(self):
ec8eba
+        """
ec8eba
+        :py:obj:`load_privatekey` raises :py:obj:`ValueError` if the passphrase
ec8eba
+        callback returns something other than a byte string.
ec8eba
+        """
ec8eba
+        self.assertRaises(
ec8eba
+            ValueError,
ec8eba
+            load_privatekey,
ec8eba
+            FILETYPE_PEM, encryptedPrivateKeyPEM, lambda *args: 3)
ec8eba
+
ec8eba
+
ec8eba
     def test_dump_privatekey_wrong_args(self):
ec8eba
         """
ec8eba
         L{dump_privatekey} raises L{TypeError} if called with the wrong number
ec8eba
         of arguments.
ec8eba
         """
ec8eba
         self.assertRaises(TypeError, dump_privatekey)
ec8eba
+        # If cipher name is given, password is required.
ec8eba
+        self.assertRaises(
ec8eba
+            ValueError, dump_privatekey, FILETYPE_PEM, PKey(), "foo")
ec8eba
 
ec8eba
 
ec8eba
     def test_dump_privatekey_unknown_cipher(self):
ec8eba
@@ -2138,6 +2161,18 @@ class FunctionTests(TestCase):
ec8eba
         self.assertRaises(ValueError, dump_privatekey, 100, key)
ec8eba
 
ec8eba
 
ec8eba
+    def test_load_privatekey_passphraseCallbackLength(self):
ec8eba
+        """
ec8eba
+        :py:obj:`crypto.load_privatekey` should raise an error when the passphrase
ec8eba
+        provided by the callback is too long, not silently truncate it.
ec8eba
+        """
ec8eba
+        def cb(ignored):
ec8eba
+            return "a" * 1025
ec8eba
+
ec8eba
+        self.assertRaises(ValueError,
ec8eba
+            load_privatekey, FILETYPE_PEM, encryptedPrivateKeyPEM, cb)
ec8eba
+
ec8eba
+
ec8eba
     def test_dump_privatekey_passphrase(self):
ec8eba
         """
ec8eba
         L{dump_privatekey} writes an encrypted PEM when given a passphrase.
ec8eba
@@ -2152,6 +2187,17 @@ class FunctionTests(TestCase):
ec8eba
         self.assertEqual(loadedKey.bits(), key.bits())
ec8eba
 
ec8eba
 
ec8eba
+    def test_dump_privatekey_passphraseWrongType(self):
ec8eba
+        """
ec8eba
+        :py:obj:`dump_privatekey` raises :py:obj:`ValueError` when it is passed a passphrase
ec8eba
+        with a private key encoded in a format, that doesn't support
ec8eba
+        encryption.
ec8eba
+        """
ec8eba
+        key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
ec8eba
+        self.assertRaises(ValueError,
ec8eba
+            dump_privatekey, FILETYPE_ASN1, key, "blowfish", "secret")
ec8eba
+
ec8eba
+
ec8eba
     def test_dump_certificate(self):
ec8eba
         """
ec8eba
         L{dump_certificate} writes PEM, DER, and text.
ec8eba
@@ -2230,6 +2276,32 @@ class FunctionTests(TestCase):
ec8eba
         self.assertEqual(loadedKey.bits(), key.bits())
ec8eba
 
ec8eba
 
ec8eba
+    def test_dump_privatekey_passphrase_exception(self):
ec8eba
+        """
ec8eba
+        :py:obj:`dump_privatekey` should not overwrite the exception raised
ec8eba
+        by the passphrase callback.
ec8eba
+        """
ec8eba
+        def cb(ignored):
ec8eba
+            raise ArithmeticError
ec8eba
+
ec8eba
+        key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
ec8eba
+        self.assertRaises(ArithmeticError,
ec8eba
+            dump_privatekey, FILETYPE_PEM, key, "blowfish", cb)
ec8eba
+
ec8eba
+
ec8eba
+    def test_dump_privatekey_passphraseCallbackLength(self):
ec8eba
+        """
ec8eba
+        :py:obj:`crypto.dump_privatekey` should raise an error when the passphrase
ec8eba
+        provided by the callback is too long, not silently truncate it.
ec8eba
+        """
ec8eba
+        def cb(ignored):
ec8eba
+            return "a" * 1025
ec8eba
+
ec8eba
+        key = load_privatekey(FILETYPE_PEM, cleartextPrivateKeyPEM)
ec8eba
+        self.assertRaises(ValueError,
ec8eba
+            dump_privatekey, FILETYPE_PEM, key, "blowfish", cb)
ec8eba
+
ec8eba
+
ec8eba
     def test_load_pkcs7_data(self):
ec8eba
         """
ec8eba
         L{load_pkcs7_data} accepts a PKCS#7 string and returns an instance of