[Python-checkins] cpython: Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.

christian.heimes python-checkins at python.org
Mon Sep 5 17:55:04 EDT 2016


https://hg.python.org/cpython/rev/74ce062a0397
changeset:   103072:74ce062a0397
user:        Christian Heimes <christian at python.org>
date:        Mon Sep 05 23:54:41 2016 +0200
summary:
  Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.

files:
  Doc/library/socket.rst  |   55 +++-
  Lib/test/test_socket.py |  165 ++++++++++
  Misc/NEWS               |    2 +
  Modules/socketmodule.c  |  437 ++++++++++++++++++++++++---
  configure               |   49 ++-
  configure.ac            |   13 +
  pyconfig.h.in           |    3 +
  7 files changed, 649 insertions(+), 75 deletions(-)


diff --git a/Doc/library/socket.rst b/Doc/library/socket.rst
--- a/Doc/library/socket.rst
+++ b/Doc/library/socket.rst
@@ -131,6 +131,22 @@
     string format. (ex. ``b'12:23:34:45:56:67'``) This protocol is not
     supported under FreeBSD.
 
+- :const:`AF_ALG` is a Linux-only socket based interface to Kernel
+  cryptography. An algorithm socket is configured with a tuple of two to four
+  elements ``(type, name [, feat [, mask]])``, where:
+
+  - *type* is the algorithm type as string, e.g. ``aead``, ``hash``,
+   ``skcipher`` or ``rng``.
+
+  - *name* is the algorithm name and operation mode as string, e.g.
+    ``sha256``, ``hmac(sha256)``, ``cbc(aes)`` or ``drbg_nopr_ctr_aes256``.
+
+  - *feat* and *mask* are unsigned 32bit integers.
+
+  Availability Linux 2.6.38, some algorithm types require more recent Kernels.
+
+  .. versionadded:: 3.6
+
 - Certain other address families (:const:`AF_PACKET`, :const:`AF_CAN`)
   support specific representations.
 
@@ -350,6 +366,16 @@
    TIPC related constants, matching the ones exported by the C socket API. See
    the TIPC documentation for more information.
 
+.. data:: AF_ALG
+          SOL_ALG
+          ALG_*
+
+   Constants for Linux Kernel cryptography.
+
+   Availability: Linux >= 2.6.38.
+
+   .. versionadded:: 3.6
+
 .. data:: AF_LINK
 
   Availability: BSD, OSX.
@@ -1294,6 +1320,15 @@
       an exception, the method now retries the system call instead of raising
       an :exc:`InterruptedError` exception (see :pep:`475` for the rationale).
 
+.. method:: socket.sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags]]])
+
+   Specialized version of :meth:`~socket.sendmsg` for :const:`AF_ALG` socket.
+   Set mode, IV, AEAD associated data length and flags for :const:`AF_ALG` socket.
+
+   Availability: Linux >= 2.6.38
+
+   .. versionadded:: 3.6
+
 .. method:: socket.sendfile(file, offset=0, count=None)
 
    Send a file until EOF is reached by using high-performance
@@ -1342,21 +1377,29 @@
    For further information, please consult the :ref:`notes on socket timeouts <socket-timeouts>`.
 
 
-.. method:: socket.setsockopt(level, optname, value)
+.. method:: socket.setsockopt(level, optname, value: int)
+.. method:: socket.setsockopt(level, optname, value: buffer)
+.. method:: socket.setsockopt(level, optname, None, optlen: int)
 
    .. index:: module: struct
 
    Set the value of the given socket option (see the Unix manual page
    :manpage:`setsockopt(2)`).  The needed symbolic constants are defined in the
-   :mod:`socket` module (:const:`SO_\*` etc.).  The value can be an integer or
-   a :term:`bytes-like object` representing a buffer.  In the latter case it is
-   up to the caller to
-   ensure that the bytestring contains the proper bits (see the optional built-in
-   module :mod:`struct` for a way to encode C structures as bytestrings).
+   :mod:`socket` module (:const:`SO_\*` etc.).  The value can be an integer,
+   None or a :term:`bytes-like object` representing a buffer. In the later
+   case it is up to the caller to ensure that the bytestring contains the
+   proper bits (see the optional built-in module :mod:`struct` for a way to
+   encode C structures as bytestrings). When value is set to None,
+   optlen argument is required. It's equivalent to call setsockopt C
+   function with optval=NULL and optlen=optlen.
+
 
    .. versionchanged:: 3.5
       Writable :term:`bytes-like object` is now accepted.
 
+   .. versionchanged:: 3.6
+      setsockopt(level, optname, None, optlen: int) form added.
+
 
 .. method:: socket.shutdown(how)
 
diff --git a/Lib/test/test_socket.py b/Lib/test/test_socket.py
--- a/Lib/test/test_socket.py
+++ b/Lib/test/test_socket.py
@@ -5325,6 +5325,170 @@
     def meth_from_sock(self, sock):
         return getattr(sock, "_sendfile_use_sendfile")
 
+ at unittest.skipUnless(hasattr(socket, "AF_ALG"), 'AF_ALG required')
+class LinuxKernelCryptoAPI(unittest.TestCase):
+    # tests for AF_ALG
+    def create_alg(self, typ, name):
+        sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
+        sock.bind((typ, name))
+        return sock
+
+    def test_sha256(self):
+        expected = bytes.fromhex("ba7816bf8f01cfea414140de5dae2223b00361a396"
+                                 "177a9cb410ff61f20015ad")
+        with self.create_alg('hash', 'sha256') as algo:
+            op, _ = algo.accept()
+            with op:
+                op.sendall(b"abc")
+                self.assertEqual(op.recv(512), expected)
+
+            op, _ = algo.accept()
+            with op:
+                op.send(b'a', socket.MSG_MORE)
+                op.send(b'b', socket.MSG_MORE)
+                op.send(b'c', socket.MSG_MORE)
+                op.send(b'')
+                self.assertEqual(op.recv(512), expected)
+
+    def test_hmac_sha1(self):
+        expected = bytes.fromhex("effcdf6ae5eb2fa2d27416d5f184df9c259a7c79")
+        with self.create_alg('hash', 'hmac(sha1)') as algo:
+            algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, b"Jefe")
+            op, _ = algo.accept()
+            with op:
+                op.sendall(b"what do ya want for nothing?")
+                self.assertEqual(op.recv(512), expected)
+
+    def test_aes_cbc(self):
+        key = bytes.fromhex('06a9214036b8a15b512e03d534120006')
+        iv = bytes.fromhex('3dafba429d9eb430b422da802c9fac41')
+        msg = b"Single block msg"
+        ciphertext = bytes.fromhex('e353779c1079aeb82708942dbe77181a')
+        msglen = len(msg)
+        with self.create_alg('skcipher', 'cbc(aes)') as algo:
+            algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, key)
+            op, _ = algo.accept()
+            with op:
+                op.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, iv=iv,
+                                 flags=socket.MSG_MORE)
+                op.sendall(msg)
+                self.assertEqual(op.recv(msglen), ciphertext)
+
+            op, _ = algo.accept()
+            with op:
+                op.sendmsg_afalg([ciphertext],
+                                 op=socket.ALG_OP_DECRYPT, iv=iv)
+                self.assertEqual(op.recv(msglen), msg)
+
+            # long message
+            multiplier = 1024
+            longmsg = [msg] * multiplier
+            op, _ = algo.accept()
+            with op:
+                op.sendmsg_afalg(longmsg,
+                                 op=socket.ALG_OP_ENCRYPT, iv=iv)
+                enc = op.recv(msglen * multiplier)
+            self.assertEqual(len(enc), msglen * multiplier)
+            self.assertTrue(enc[:msglen], ciphertext)
+
+            op, _ = algo.accept()
+            with op:
+                op.sendmsg_afalg([enc],
+                                 op=socket.ALG_OP_DECRYPT, iv=iv)
+                dec = op.recv(msglen * multiplier)
+            self.assertEqual(len(dec), msglen * multiplier)
+            self.assertEqual(dec, msg * multiplier)
+
+    @support.requires_linux_version(3, 19)
+    def test_aead_aes_gcm(self):
+        key = bytes.fromhex('c939cc13397c1d37de6ae0e1cb7c423c')
+        iv = bytes.fromhex('b3d8cc017cbb89b39e0f67e2')
+        plain = bytes.fromhex('c3b3c41f113a31b73d9a5cd432103069')
+        assoc = bytes.fromhex('24825602bd12a984e0092d3e448eda5f')
+        expected_ct = bytes.fromhex('93fe7d9e9bfd10348a5606e5cafa7354')
+        expected_tag = bytes.fromhex('0032a1dc85f1c9786925a2e71d8272dd')
+
+        taglen = len(expected_tag)
+        assoclen = len(assoc)
+
+        with self.create_alg('aead', 'gcm(aes)') as algo:
+            algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, key)
+            algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_AEAD_AUTHSIZE,
+                            None, taglen)
+
+            # send assoc, plain and tag buffer in separate steps
+            op, _ = algo.accept()
+            with op:
+                op.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, iv=iv,
+                                 assoclen=assoclen, flags=socket.MSG_MORE)
+                op.sendall(assoc, socket.MSG_MORE)
+                op.sendall(plain, socket.MSG_MORE)
+                op.sendall(b'\x00' * taglen)
+                res = op.recv(assoclen + len(plain) + taglen)
+                self.assertEqual(expected_ct, res[assoclen:-taglen])
+                self.assertEqual(expected_tag, res[-taglen:])
+
+            # now with msg
+            op, _ = algo.accept()
+            with op:
+                msg = assoc + plain + b'\x00' * taglen
+                op.sendmsg_afalg([msg], op=socket.ALG_OP_ENCRYPT, iv=iv,
+                                 assoclen=assoclen)
+                res = op.recv(assoclen + len(plain) + taglen)
+                self.assertEqual(expected_ct, res[assoclen:-taglen])
+                self.assertEqual(expected_tag, res[-taglen:])
+
+            # create anc data manually
+            pack_uint32 = struct.Struct('I').pack
+            op, _ = algo.accept()
+            with op:
+                msg = assoc + plain + b'\x00' * taglen
+                op.sendmsg(
+                    [msg],
+                    ([socket.SOL_ALG, socket.ALG_SET_OP, pack_uint32(socket.ALG_OP_ENCRYPT)],
+                     [socket.SOL_ALG, socket.ALG_SET_IV, pack_uint32(len(iv)) + iv],
+                     [socket.SOL_ALG, socket.ALG_SET_AEAD_ASSOCLEN, pack_uint32(assoclen)],
+                    )
+                )
+                res = op.recv(len(msg))
+                self.assertEqual(expected_ct, res[assoclen:-taglen])
+                self.assertEqual(expected_tag, res[-taglen:])
+
+            # decrypt and verify
+            op, _ = algo.accept()
+            with op:
+                msg = assoc + expected_ct + expected_tag
+                op.sendmsg_afalg([msg], op=socket.ALG_OP_DECRYPT, iv=iv,
+                                 assoclen=assoclen)
+                res = op.recv(len(msg))
+                self.assertEqual(plain, res[assoclen:-taglen])
+
+    def test_drbg_pr_sha256(self):
+        # deterministic random bit generator, prediction resistance, sha256
+        with self.create_alg('rng', 'drbg_pr_sha256') as algo:
+            extra_seed = os.urandom(32)
+            algo.setsockopt(socket.SOL_ALG, socket.ALG_SET_KEY, extra_seed)
+            op, _ = algo.accept()
+            with op:
+                rn = op.recv(32)
+                self.assertEqual(len(rn), 32)
+
+    def test_sendmsg_afalg_args(self):
+        sock = socket.socket(socket.AF_ALG, socket.SOCK_SEQPACKET, 0)
+        with self.assertRaises(TypeError):
+            sock.sendmsg_afalg()
+
+        with self.assertRaises(TypeError):
+            sock.sendmsg_afalg(op=None)
+
+        with self.assertRaises(TypeError):
+            sock.sendmsg_afalg(1)
+
+        with self.assertRaises(TypeError):
+            sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=None)
+
+        with self.assertRaises(TypeError):
+            sock.sendmsg_afalg(op=socket.ALG_OP_ENCRYPT, assoclen=-1)
 
 def test_main():
     tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
@@ -5352,6 +5516,7 @@
     tests.extend([TIPCTest, TIPCThreadableTest])
     tests.extend([BasicCANTest, CANTest])
     tests.extend([BasicRDSTest, RDSTest])
+    tests.append(LinuxKernelCryptoAPI)
     tests.extend([
         CmsgMacroTests,
         SendmsgUDPTest,
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -77,6 +77,8 @@
 Library
 -------
 
+- Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.
+
 - Issue #26470: Port ssl and hashlib module to OpenSSL 1.1.0.
 
 - Issue #11620: Fix support for SND_MEMORY in winsound.PlaySound.  Based on a
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -136,7 +136,7 @@
 send(data[, flags]) -- send data, may not send all of it\n\
 sendto(data[, flags], addr) -- send data to a given address\n\
 setblocking(0 | 1) -- set or clear the blocking I/O flag\n\
-setsockopt(level, optname, value) -- set socket options\n\
+setsockopt(level, optname, value[, optlen]) -- set socket options\n\
 settimeout(None | float) -- set or clear the timeout\n\
 shutdown(how) -- shut down traffic in one or both directions\n\
 if_nameindex() -- return all network interface indices and names\n\
@@ -286,6 +286,36 @@
 #include <net/if.h>
 #endif
 
+#ifdef HAVE_SOCKADDR_ALG
+#include <linux/if_alg.h>
+#ifndef AF_ALG
+#define AF_ALG 38
+#endif
+#ifndef SOL_ALG
+#define SOL_ALG 279
+#endif
+
+/* Linux 3.19 */
+#ifndef ALG_SET_AEAD_ASSOCLEN
+#define ALG_SET_AEAD_ASSOCLEN           4
+#endif
+#ifndef ALG_SET_AEAD_AUTHSIZE
+#define ALG_SET_AEAD_AUTHSIZE           5
+#endif
+/* Linux 4.8 */
+#ifndef ALG_SET_PUBKEY
+#define ALG_SET_PUBKEY                  6
+#endif
+
+#ifndef ALG_OP_SIGN
+#define ALG_OP_SIGN                     2
+#endif
+#ifndef ALG_OP_VERIFY
+#define ALG_OP_VERIFY                   3
+#endif
+
+#endif /* HAVE_SOCKADDR_ALG */
+
 /* Generic socket object definitions and includes */
 #define PySocket_BUILDING_SOCKET
 #include "socketmodule.h"
@@ -1375,6 +1405,22 @@
         }
 #endif
 
+#ifdef HAVE_SOCKADDR_ALG
+    case AF_ALG:
+    {
+        struct sockaddr_alg *a = (struct sockaddr_alg *)addr;
+        return Py_BuildValue("s#s#HH",
+            a->salg_type,
+            strnlen((const char*)a->salg_type,
+                    sizeof(a->salg_type)),
+            a->salg_name,
+            strnlen((const char*)a->salg_name,
+                    sizeof(a->salg_name)),
+            a->salg_feat,
+            a->salg_mask);
+    }
+#endif
+
     /* More cases here... */
 
     default:
@@ -1940,6 +1986,36 @@
             return 0;
         }
 #endif
+#ifdef HAVE_SOCKADDR_ALG
+    case AF_ALG:
+    {
+        struct sockaddr_alg *sa;
+        char *type;
+        char *name;
+        sa = (struct sockaddr_alg *)addr_ret;
+
+        memset(sa, 0, sizeof(*sa));
+        sa->salg_family = AF_ALG;
+
+        if (!PyArg_ParseTuple(args, "ss|HH:getsockaddrarg",
+                                &type, &name, &sa->salg_feat, &sa->salg_mask))
+            return 0;
+        /* sockaddr_alg has fixed-sized char arrays for type and name */
+        if (strlen(type) > sizeof(sa->salg_type)) {
+            PyErr_SetString(PyExc_ValueError, "AF_ALG type too long.");
+            return 0;
+        }
+        strncpy((char *)sa->salg_type, type, sizeof(sa->salg_type));
+        if (strlen(name) > sizeof(sa->salg_name)) {
+            PyErr_SetString(PyExc_ValueError, "AF_ALG name too long.");
+            return 0;
+        }
+        strncpy((char *)sa->salg_name, name, sizeof(sa->salg_name));
+
+        *len_ret = sizeof(*sa);
+        return 1;
+    }
+#endif
 
     /* More cases here... */
 
@@ -2061,6 +2137,13 @@
             return 0;
         }
 #endif
+#ifdef HAVE_SOCKADDR_ALG
+    case AF_ALG:
+    {
+        *len_ret = sizeof (struct sockaddr_alg);
+        return 1;
+    }
+#endif
 
     /* More cases here... */
 
@@ -2220,10 +2303,21 @@
 sock_accept_impl(PySocketSockObject *s, void *data)
 {
     struct sock_accept *ctx = data;
+    struct sockaddr *addr = SAS2SA(ctx->addrbuf);
+    socklen_t *paddrlen = ctx->addrlen;
+#ifdef HAVE_SOCKADDR_ALG
+    /* AF_ALG does not support accept() with addr and raises
+     * ECONNABORTED instead. */
+    if (s->sock_family == AF_ALG) {
+        addr = NULL;
+        paddrlen = NULL;
+        *ctx->addrlen = 0;
+    }
+#endif
 
 #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
     if (accept4_works != 0) {
-        ctx->result = accept4(s->sock_fd, SAS2SA(ctx->addrbuf), ctx->addrlen,
+        ctx->result = accept4(s->sock_fd, addr, paddrlen,
                               SOCK_CLOEXEC);
         if (ctx->result == INVALID_SOCKET && accept4_works == -1) {
             /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
@@ -2231,9 +2325,9 @@
         }
     }
     if (accept4_works == 0)
-        ctx->result = accept(s->sock_fd, SAS2SA(ctx->addrbuf), ctx->addrlen);
+        ctx->result = accept(s->sock_fd, addr, paddrlen);
 #else
-    ctx->result = accept(s->sock_fd, SAS2SA(ctx->addrbuf), ctx->addrlen);
+    ctx->result = accept(s->sock_fd, addr, paddrlen);
 #endif
 
 #ifdef MS_WINDOWS
@@ -2435,9 +2529,12 @@
 operations are disabled.");
 
 /* s.setsockopt() method.
-   With an integer third argument, sets an integer option.
+   With an integer third argument, sets an integer optval with optlen=4.
+   With None as third argument and an integer fourth argument, set
+   optval=NULL with unsigned int as optlen.
    With a string third argument, sets an option from a buffer;
-   use optional built-in module 'struct' to encode the string. */
+   use optional built-in module 'struct' to encode the string.
+*/
 
 static PyObject *
 sock_setsockopt(PySocketSockObject *s, PyObject *args)
@@ -2447,32 +2544,49 @@
     int res;
     Py_buffer optval;
     int flag;
-
+    unsigned int optlen;
+    PyObject *none;
+
+    /* setsockopt(level, opt, flag) */
     if (PyArg_ParseTuple(args, "iii:setsockopt",
                          &level, &optname, &flag)) {
         res = setsockopt(s->sock_fd, level, optname,
                          (char*)&flag, sizeof flag);
-    }
-    else {
-        PyErr_Clear();
-        if (!PyArg_ParseTuple(args, "iiy*:setsockopt",
-                              &level, &optname, &optval))
-            return NULL;
+        goto done;
+    }
+
+    PyErr_Clear();
+    /* setsockopt(level, opt, (None, flag)) */
+    if (PyArg_ParseTuple(args, "iiO!I:setsockopt",
+                         &level, &optname, Py_TYPE(Py_None), &none, &optlen)) {
+        assert(sizeof(socklen_t) >= sizeof(unsigned int));
+        res = setsockopt(s->sock_fd, level, optname,
+                         NULL, (socklen_t)optlen);
+        goto done;
+    }
+
+    PyErr_Clear();
+    /* setsockopt(level, opt, buffer) */
+    if (!PyArg_ParseTuple(args, "iiy*:setsockopt",
+                            &level, &optname, &optval))
+        return NULL;
+
 #ifdef MS_WINDOWS
-        if (optval.len > INT_MAX) {
-            PyBuffer_Release(&optval);
-            PyErr_Format(PyExc_OverflowError,
-                         "socket option is larger than %i bytes",
-                         INT_MAX);
-            return NULL;
-        }
-        res = setsockopt(s->sock_fd, level, optname,
-                         optval.buf, (int)optval.len);
+    if (optval.len > INT_MAX) {
+        PyBuffer_Release(&optval);
+        PyErr_Format(PyExc_OverflowError,
+                        "socket option is larger than %i bytes",
+                        INT_MAX);
+        return NULL;
+    }
+    res = setsockopt(s->sock_fd, level, optname,
+                        optval.buf, (int)optval.len);
 #else
-        res = setsockopt(s->sock_fd, level, optname, optval.buf, optval.len);
-#endif
-        PyBuffer_Release(&optval);
-    }
+    res = setsockopt(s->sock_fd, level, optname, optval.buf, optval.len);
+#endif
+    PyBuffer_Release(&optval);
+
+done:
     if (res < 0) {
         return s->errorhandler();
     }
@@ -2481,10 +2595,13 @@
 }
 
 PyDoc_STRVAR(setsockopt_doc,
-"setsockopt(level, option, value)\n\
+"setsockopt(level, option, value: int)\n\
+setsockopt(level, option, value: buffer)\n\
+setsockopt(level, option, None, optlen: int)\n\
 \n\
 Set a socket option.  See the Unix manual for level and option.\n\
-The value argument can either be an integer or a string.");
+The value argument can either be an integer, a string buffer, or \n\
+None, optlen.");
 
 
 /* s.getsockopt() method.
@@ -3774,6 +3891,51 @@
 };
 
 static int
+sock_sendmsg_iovec(PySocketSockObject *s, PyObject *data_arg,
+                   struct msghdr *msg,
+                   Py_buffer **databufsout, Py_ssize_t *ndatabufsout) {
+    Py_ssize_t ndataparts, ndatabufs = 0;
+    int result = -1;
+    struct iovec *iovs = NULL;
+    PyObject *data_fast = NULL;
+    Py_buffer *databufs = NULL;
+
+    /* Fill in an iovec for each message part, and save the Py_buffer
+       structs to release afterwards. */
+    if ((data_fast = PySequence_Fast(data_arg,
+                                     "sendmsg() argument 1 must be an "
+                                     "iterable")) == NULL)
+        goto finally;
+    ndataparts = PySequence_Fast_GET_SIZE(data_fast);
+    if (ndataparts > INT_MAX) {
+        PyErr_SetString(PyExc_OSError, "sendmsg() argument 1 is too long");
+        goto finally;
+    }
+    msg->msg_iovlen = ndataparts;
+    if (ndataparts > 0 &&
+        ((msg->msg_iov = iovs = PyMem_New(struct iovec, ndataparts)) == NULL ||
+         (databufs = PyMem_New(Py_buffer, ndataparts)) == NULL)) {
+        PyErr_NoMemory();
+        goto finally;
+    }
+    for (; ndatabufs < ndataparts; ndatabufs++) {
+        if (!PyArg_Parse(PySequence_Fast_GET_ITEM(data_fast, ndatabufs),
+                         "y*;sendmsg() argument 1 must be an iterable of "
+                         "bytes-like objects",
+                         &databufs[ndatabufs]))
+            goto finally;
+        iovs[ndatabufs].iov_base = databufs[ndatabufs].buf;
+        iovs[ndatabufs].iov_len = databufs[ndatabufs].len;
+    }
+    result = 0;
+  finally:
+    *databufsout = databufs;
+    *ndatabufsout = ndatabufs;
+    Py_XDECREF(data_fast);
+    return result;
+}
+
+static int
 sock_sendmsg_impl(PySocketSockObject *s, void *data)
 {
     struct sock_sendmsg *ctx = data;
@@ -3787,9 +3949,8 @@
 static PyObject *
 sock_sendmsg(PySocketSockObject *s, PyObject *args)
 {
-    Py_ssize_t i, ndataparts, ndatabufs = 0, ncmsgs, ncmsgbufs = 0;
+    Py_ssize_t i, ndatabufs = 0, ncmsgs, ncmsgbufs = 0;
     Py_buffer *databufs = NULL;
-    struct iovec *iovs = NULL;
     sock_addr_t addrbuf;
     struct msghdr msg = {0};
     struct cmsginfo {
@@ -3800,7 +3961,7 @@
     void *controlbuf = NULL;
     size_t controllen, controllen_last;
     int addrlen, flags = 0;
-    PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL, *data_fast = NULL,
+    PyObject *data_arg, *cmsg_arg = NULL, *addr_arg = NULL,
         *cmsg_fast = NULL, *retval = NULL;
     struct sock_sendmsg ctx;
 
@@ -3818,30 +3979,8 @@
 
     /* Fill in an iovec for each message part, and save the Py_buffer
        structs to release afterwards. */
-    if ((data_fast = PySequence_Fast(data_arg,
-                                     "sendmsg() argument 1 must be an "
-                                     "iterable")) == NULL)
+    if (sock_sendmsg_iovec(s, data_arg, &msg, &databufs, &ndatabufs) == -1) {
         goto finally;
-    ndataparts = PySequence_Fast_GET_SIZE(data_fast);
-    if (ndataparts > INT_MAX) {
-        PyErr_SetString(PyExc_OSError, "sendmsg() argument 1 is too long");
-        goto finally;
-    }
-    msg.msg_iovlen = ndataparts;
-    if (ndataparts > 0 &&
-        ((msg.msg_iov = iovs = PyMem_New(struct iovec, ndataparts)) == NULL ||
-         (databufs = PyMem_New(Py_buffer, ndataparts)) == NULL)) {
-        PyErr_NoMemory();
-        goto finally;
-    }
-    for (; ndatabufs < ndataparts; ndatabufs++) {
-        if (!PyArg_Parse(PySequence_Fast_GET_ITEM(data_fast, ndatabufs),
-                         "y*;sendmsg() argument 1 must be an iterable of "
-                         "bytes-like objects",
-                         &databufs[ndatabufs]))
-            goto finally;
-        iovs[ndatabufs].iov_base = databufs[ndatabufs].buf;
-        iovs[ndatabufs].iov_len = databufs[ndatabufs].len;
     }
 
     if (cmsg_arg == NULL)
@@ -3972,8 +4111,6 @@
     for (i = 0; i < ndatabufs; i++)
         PyBuffer_Release(&databufs[i]);
     PyMem_Free(databufs);
-    PyMem_Free(iovs);
-    Py_XDECREF(data_fast);
     return retval;
 }
 
@@ -3995,6 +4132,165 @@
 data sent.");
 #endif    /* CMSG_LEN */
 
+#ifdef HAVE_SOCKADDR_ALG
+static PyObject*
+sock_sendmsg_afalg(PySocketSockObject *self, PyObject *args, PyObject *kwds)
+{
+    PyObject *retval = NULL;
+
+    Py_ssize_t i, ndatabufs = 0;
+    Py_buffer *databufs = NULL;
+    PyObject *data_arg = NULL;
+
+    Py_buffer iv = {NULL, NULL};
+
+    PyObject *opobj = NULL;
+    int op = -1;
+
+    PyObject *assoclenobj = NULL;
+    int assoclen = -1;
+
+    unsigned int *uiptr;
+    int flags = 0;
+
+    struct msghdr msg;
+    struct cmsghdr *header = NULL;
+    struct af_alg_iv *alg_iv = NULL;
+    struct sock_sendmsg ctx;
+    Py_ssize_t controllen;
+    void *controlbuf = NULL;
+    static char *keywords[] = {"msg", "op", "iv", "assoclen", "flags", 0};
+
+    if (self->sock_family != AF_ALG) {
+        PyErr_SetString(PyExc_OSError,
+                        "algset is only supported for AF_ALG");
+        return NULL;
+    }
+
+    if (!PyArg_ParseTupleAndKeywords(args, kwds,
+                                     "|O$O!y*O!i:sendmsg_afalg", keywords,
+                                     &data_arg,
+                                     &PyLong_Type, &opobj, &iv,
+                                     &PyLong_Type, &assoclenobj, &flags))
+        return NULL;
+
+    /* op is a required, keyword-only argument >= 0 */
+    if (opobj != NULL) {
+        op = _PyLong_AsInt(opobj);
+    }
+    if (op < 0) {
+        /* override exception from _PyLong_AsInt() */
+        PyErr_SetString(PyExc_TypeError,
+                        "Invalid or missing argument 'op'");
+        goto finally;
+    }
+    /* assoclen is optional but must be >= 0 */
+    if (assoclenobj != NULL) {
+        assoclen = _PyLong_AsInt(assoclenobj);
+        if (assoclen == -1 && PyErr_Occurred()) {
+            goto finally;
+        }
+        if (assoclen < 0) {
+            PyErr_SetString(PyExc_TypeError,
+                            "assoclen must be positive");
+            goto finally;
+        }
+    }
+
+    controllen = CMSG_SPACE(4);
+    if (iv.buf != NULL) {
+        controllen += CMSG_SPACE(sizeof(*alg_iv) + iv.len);
+    }
+    if (assoclen >= 0) {
+        controllen += CMSG_SPACE(4);
+    }
+
+    controlbuf = PyMem_Malloc(controllen);
+    if (controlbuf == NULL) {
+        return PyErr_NoMemory();
+    }
+    memset(controlbuf, 0, controllen);
+
+    memset(&msg, 0, sizeof(msg));
+    msg.msg_controllen = controllen;
+    msg.msg_control = controlbuf;
+
+    /* Fill in an iovec for each message part, and save the Py_buffer
+       structs to release afterwards. */
+    if (data_arg != NULL) {
+        if (sock_sendmsg_iovec(self, data_arg, &msg, &databufs, &ndatabufs) == -1) {
+            goto finally;
+        }
+    }
+
+    /* set operation to encrypt or decrypt */
+    header = CMSG_FIRSTHDR(&msg);
+    if (header == NULL) {
+        PyErr_SetString(PyExc_RuntimeError,
+                        "unexpected NULL result from CMSG_FIRSTHDR");
+        goto finally;
+    }
+    header->cmsg_level = SOL_ALG;
+    header->cmsg_type = ALG_SET_OP;
+    header->cmsg_len = CMSG_LEN(4);
+    uiptr = (void*)CMSG_DATA(header);
+    *uiptr = (unsigned int)op;
+
+    /* set initialization vector */
+    if (iv.buf != NULL) {
+        header = CMSG_NXTHDR(&msg, header);
+        if (header == NULL) {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "unexpected NULL result from CMSG_NXTHDR(iv)");
+            goto finally;
+        }
+        header->cmsg_level = SOL_ALG;
+        header->cmsg_type = ALG_SET_IV;
+        header->cmsg_len = CMSG_SPACE(sizeof(*alg_iv) + iv.len);
+        alg_iv = (void*)CMSG_DATA(header);
+        alg_iv->ivlen = iv.len;
+        memcpy(alg_iv->iv, iv.buf, iv.len);
+    }
+
+    /* set length of associated data for AEAD */
+    if (assoclen >= 0) {
+        header = CMSG_NXTHDR(&msg, header);
+        if (header == NULL) {
+            PyErr_SetString(PyExc_RuntimeError,
+                            "unexpected NULL result from CMSG_NXTHDR(assoc)");
+            goto finally;
+        }
+        header->cmsg_level = SOL_ALG;
+        header->cmsg_type = ALG_SET_AEAD_ASSOCLEN;
+        header->cmsg_len = CMSG_LEN(4);
+        uiptr = (void*)CMSG_DATA(header);
+        *uiptr = (unsigned int)assoclen;
+    }
+
+    ctx.msg = &msg;
+    ctx.flags = flags;
+    if (sock_call(self, 1, sock_sendmsg_impl, &ctx) < 0)
+        goto finally;
+
+    retval = PyLong_FromSsize_t(ctx.result);
+
+  finally:
+    PyMem_Free(controlbuf);
+    if (iv.buf != NULL) {
+        PyBuffer_Release(&iv);
+    }
+    for (i = 0; i < ndatabufs; i++)
+        PyBuffer_Release(&databufs[i]);
+    PyMem_Free(databufs);
+    return retval;
+}
+
+PyDoc_STRVAR(sendmsg_afalg_doc,
+"sendmsg_afalg([msg], *, op[, iv[, assoclen[, flags=MSG_MORE]]])\n\
+\n\
+Set operation mode, IV and length of associated data for an AF_ALG\n\
+operation socket.");
+#endif
 
 /* s.shutdown(how) method */
 
@@ -4174,6 +4470,10 @@
     {"sendmsg",           (PyCFunction)sock_sendmsg, METH_VARARGS,
                       sendmsg_doc},
 #endif
+#ifdef HAVE_SOCKADDR_ALG
+    {"sendmsg_afalg",     (PyCFunction)sock_sendmsg_afalg, METH_VARARGS | METH_KEYWORDS,
+                      sendmsg_afalg_doc},
+#endif
     {NULL,                      NULL}           /* sentinel */
 };
 
@@ -6277,6 +6577,9 @@
     /* Reserved for Werner's ATM */
     PyModule_AddIntMacro(m, AF_AAL5);
 #endif
+#ifdef HAVE_SOCKADDR_ALG
+    PyModule_AddIntMacro(m, AF_ALG); /* Linux crypto */
+#endif
 #ifdef AF_X25
     /* Reserved for X.25 project */
     PyModule_AddIntMacro(m, AF_X25);
@@ -6338,6 +6641,9 @@
 #ifdef NETLINK_TAPBASE
     PyModule_AddIntMacro(m, NETLINK_TAPBASE);
 #endif
+#ifdef NETLINK_CRYPTO
+    PyModule_AddIntMacro(m, NETLINK_CRYPTO);
+#endif
 #endif /* AF_NETLINK */
 #ifdef AF_ROUTE
     /* Alias to emulate 4.4BSD */
@@ -6491,6 +6797,22 @@
     PyModule_AddIntMacro(m, TIPC_TOP_SRV);
 #endif
 
+#ifdef HAVE_SOCKADDR_ALG
+    /* Socket options */
+    PyModule_AddIntMacro(m, ALG_SET_KEY);
+    PyModule_AddIntMacro(m, ALG_SET_IV);
+    PyModule_AddIntMacro(m, ALG_SET_OP);
+    PyModule_AddIntMacro(m, ALG_SET_AEAD_ASSOCLEN);
+    PyModule_AddIntMacro(m, ALG_SET_AEAD_AUTHSIZE);
+    PyModule_AddIntMacro(m, ALG_SET_PUBKEY);
+
+    /* Operations */
+    PyModule_AddIntMacro(m, ALG_OP_DECRYPT);
+    PyModule_AddIntMacro(m, ALG_OP_ENCRYPT);
+    PyModule_AddIntMacro(m, ALG_OP_SIGN);
+    PyModule_AddIntMacro(m, ALG_OP_VERIFY);
+#endif
+
     /* Socket types */
     PyModule_AddIntMacro(m, SOCK_STREAM);
     PyModule_AddIntMacro(m, SOCK_DGRAM);
@@ -6761,6 +7083,9 @@
 #ifdef SOL_RDS
     PyModule_AddIntMacro(m, SOL_RDS);
 #endif
+#ifdef HAVE_SOCKADDR_ALG
+    PyModule_AddIntMacro(m, SOL_ALG);
+#endif
 #ifdef RDS_CANCEL_SENT_TO
     PyModule_AddIntMacro(m, RDS_CANCEL_SENT_TO);
 #endif
diff --git a/configure b/configure
--- a/configure
+++ b/configure
@@ -775,7 +775,6 @@
 docdir
 oldincludedir
 includedir
-runstatedir
 localstatedir
 sharedstatedir
 sysconfdir
@@ -886,7 +885,6 @@
 sysconfdir='${prefix}/etc'
 sharedstatedir='${prefix}/com'
 localstatedir='${prefix}/var'
-runstatedir='${localstatedir}/run'
 includedir='${prefix}/include'
 oldincludedir='/usr/include'
 docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@@ -1139,15 +1137,6 @@
   | -silent | --silent | --silen | --sile | --sil)
     silent=yes ;;
 
-  -runstatedir | --runstatedir | --runstatedi | --runstated \
-  | --runstate | --runstat | --runsta | --runst | --runs \
-  | --run | --ru | --r)
-    ac_prev=runstatedir ;;
-  -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \
-  | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \
-  | --run=* | --ru=* | --r=*)
-    runstatedir=$ac_optarg ;;
-
   -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
     ac_prev=sbindir ;;
   -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@@ -1285,7 +1274,7 @@
 for ac_var in	exec_prefix prefix bindir sbindir libexecdir datarootdir \
 		datadir sysconfdir sharedstatedir localstatedir includedir \
 		oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
-		libdir localedir mandir runstatedir
+		libdir localedir mandir
 do
   eval ac_val=\$$ac_var
   # Remove trailing slashes.
@@ -1438,7 +1427,6 @@
   --sysconfdir=DIR        read-only single-machine data [PREFIX/etc]
   --sharedstatedir=DIR    modifiable architecture-independent data [PREFIX/com]
   --localstatedir=DIR     modifiable single-machine data [PREFIX/var]
-  --runstatedir=DIR       modifiable per-process data [LOCALSTATEDIR/run]
   --libdir=DIR            object code libraries [EPREFIX/lib]
   --includedir=DIR        C header files [PREFIX/include]
   --oldincludedir=DIR     C header files for non-gcc [/usr/include]
@@ -13062,6 +13050,41 @@
 
 fi
 
+{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for sockaddr_alg" >&5
+$as_echo_n "checking for sockaddr_alg... " >&6; }
+if ${ac_cv_struct_sockaddr_alg+:} false; then :
+  $as_echo_n "(cached) " >&6
+else
+  cat confdefs.h - <<_ACEOF >conftest.$ac_ext
+/* end confdefs.h.  */
+
+#		include <sys/types.h>
+#		include <sys/socket.h>
+#		include <linux/if_alg.h>
+int
+main ()
+{
+struct sockaddr_alg s
+  ;
+  return 0;
+}
+_ACEOF
+if ac_fn_c_try_compile "$LINENO"; then :
+  ac_cv_struct_sockaddr_alg=yes
+else
+  ac_cv_struct_sockaddr_alg=no
+fi
+rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
+fi
+
+{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_struct_sockaddr_alg" >&5
+$as_echo "$ac_cv_struct_sockaddr_alg" >&6; }
+if test $ac_cv_struct_sockaddr_alg = yes; then
+
+$as_echo "#define HAVE_SOCKADDR_ALG 1" >>confdefs.h
+
+fi
+
 # checks for compiler characteristics
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether char is unsigned" >&5
diff --git a/configure.ac b/configure.ac
--- a/configure.ac
+++ b/configure.ac
@@ -3862,6 +3862,19 @@
 	AC_DEFINE(HAVE_SOCKADDR_STORAGE, 1, [struct sockaddr_storage (sys/socket.h)])
 fi
 
+AC_MSG_CHECKING(for sockaddr_alg)
+AC_CACHE_VAL(ac_cv_struct_sockaddr_alg,
+AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
+#		include <sys/types.h>
+#		include <sys/socket.h>
+#		include <linux/if_alg.h>]], [[struct sockaddr_alg s]])],
+  [ac_cv_struct_sockaddr_alg=yes],
+  [ac_cv_struct_sockaddr_alg=no]))
+AC_MSG_RESULT($ac_cv_struct_sockaddr_alg)
+if test $ac_cv_struct_sockaddr_alg = yes; then
+	AC_DEFINE(HAVE_SOCKADDR_ALG, 1, [struct sockaddr_alg (linux/if_alg.h)])
+fi
+
 # checks for compiler characteristics
 
 AC_C_CHAR_UNSIGNED
diff --git a/pyconfig.h.in b/pyconfig.h.in
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -892,6 +892,9 @@
 /* Define to 1 if you have the `snprintf' function. */
 #undef HAVE_SNPRINTF
 
+/* struct sockaddr_alg (linux/if_alg.h) */
+#undef HAVE_SOCKADDR_ALG
+
 /* Define if sockaddr has sa_len member */
 #undef HAVE_SOCKADDR_SA_LEN
 

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list