Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.

This commit is contained in:
Christian Heimes 2016-09-05 23:54:41 +02:00
parent 92a6c170e6
commit dffa3949c7
7 changed files with 647 additions and 73 deletions

View File

@ -131,6 +131,22 @@ created. Socket addresses are represented as follows:
string format. (ex. ``b'12:23:34:45:56:67'``) This protocol is not string format. (ex. ``b'12:23:34:45:56:67'``) This protocol is not
supported under FreeBSD. 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`) - Certain other address families (:const:`AF_PACKET`, :const:`AF_CAN`)
support specific representations. support specific representations.
@ -350,6 +366,16 @@ Constants
TIPC related constants, matching the ones exported by the C socket API. See TIPC related constants, matching the ones exported by the C socket API. See
the TIPC documentation for more information. 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 .. data:: AF_LINK
Availability: BSD, OSX. Availability: BSD, OSX.
@ -1294,6 +1320,15 @@ to sockets.
an exception, the method now retries the system call instead of raising an exception, the method now retries the system call instead of raising
an :exc:`InterruptedError` exception (see :pep:`475` for the rationale). 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) .. method:: socket.sendfile(file, offset=0, count=None)
Send a file until EOF is reached by using high-performance Send a file until EOF is reached by using high-performance
@ -1342,21 +1377,29 @@ to sockets.
For further information, please consult the :ref:`notes on socket timeouts <socket-timeouts>`. 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 .. index:: module: struct
Set the value of the given socket option (see the Unix manual page Set the value of the given socket option (see the Unix manual page
:manpage:`setsockopt(2)`). The needed symbolic constants are defined in the :manpage:`setsockopt(2)`). The needed symbolic constants are defined in the
:mod:`socket` module (:const:`SO_\*` etc.). The value can be an integer or :mod:`socket` module (:const:`SO_\*` etc.). The value can be an integer,
a :term:`bytes-like object` representing a buffer. In the latter case it is None or a :term:`bytes-like object` representing a buffer. In the later
up to the caller to case it is up to the caller to ensure that the bytestring contains the
ensure that the bytestring contains the proper bits (see the optional built-in proper bits (see the optional built-in module :mod:`struct` for a way to
module :mod:`struct` for a way to encode C structures as bytestrings). 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 .. versionchanged:: 3.5
Writable :term:`bytes-like object` is now accepted. Writable :term:`bytes-like object` is now accepted.
.. versionchanged:: 3.6
setsockopt(level, optname, None, optlen: int) form added.
.. method:: socket.shutdown(how) .. method:: socket.shutdown(how)

View File

@ -5325,6 +5325,170 @@ class SendfileUsingSendfileTest(SendfileUsingSendTest):
def meth_from_sock(self, sock): def meth_from_sock(self, sock):
return getattr(sock, "_sendfile_use_sendfile") return getattr(sock, "_sendfile_use_sendfile")
@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(): def test_main():
tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest, tests = [GeneralModuleTests, BasicTCPTest, TCPCloserTest, TCPTimeoutTest,
@ -5352,6 +5516,7 @@ def test_main():
tests.extend([TIPCTest, TIPCThreadableTest]) tests.extend([TIPCTest, TIPCThreadableTest])
tests.extend([BasicCANTest, CANTest]) tests.extend([BasicCANTest, CANTest])
tests.extend([BasicRDSTest, RDSTest]) tests.extend([BasicRDSTest, RDSTest])
tests.append(LinuxKernelCryptoAPI)
tests.extend([ tests.extend([
CmsgMacroTests, CmsgMacroTests,
SendmsgUDPTest, SendmsgUDPTest,

View File

@ -77,6 +77,8 @@ Core and Builtins
Library 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 #26470: Port ssl and hashlib module to OpenSSL 1.1.0.
- Issue #11620: Fix support for SND_MEMORY in winsound.PlaySound. Based on a - Issue #11620: Fix support for SND_MEMORY in winsound.PlaySound. Based on a

View File

@ -136,7 +136,7 @@ sendall(data[, flags]) -- send all data\n\
send(data[, flags]) -- send data, may not send all of it\n\ send(data[, flags]) -- send data, may not send all of it\n\
sendto(data[, flags], addr) -- send data to a given address\n\ sendto(data[, flags], addr) -- send data to a given address\n\
setblocking(0 | 1) -- set or clear the blocking I/O flag\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\ settimeout(None | float) -- set or clear the timeout\n\
shutdown(how) -- shut down traffic in one or both directions\n\ shutdown(how) -- shut down traffic in one or both directions\n\
if_nameindex() -- return all network interface indices and names\n\ if_nameindex() -- return all network interface indices and names\n\
@ -286,6 +286,36 @@ http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/net/getaddrinfo.c.diff?r1=1.82&
#include <net/if.h> #include <net/if.h>
#endif #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 */ /* Generic socket object definitions and includes */
#define PySocket_BUILDING_SOCKET #define PySocket_BUILDING_SOCKET
#include "socketmodule.h" #include "socketmodule.h"
@ -1375,6 +1405,22 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto)
} }
#endif #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... */ /* More cases here... */
default: default:
@ -1940,6 +1986,36 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args,
return 0; return 0;
} }
#endif #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... */ /* More cases here... */
@ -2061,6 +2137,13 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret)
return 0; return 0;
} }
#endif #endif
#ifdef HAVE_SOCKADDR_ALG
case AF_ALG:
{
*len_ret = sizeof (struct sockaddr_alg);
return 1;
}
#endif
/* More cases here... */ /* More cases here... */
@ -2220,10 +2303,21 @@ static int
sock_accept_impl(PySocketSockObject *s, void *data) sock_accept_impl(PySocketSockObject *s, void *data)
{ {
struct sock_accept *ctx = 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 defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC)
if (accept4_works != 0) { 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); SOCK_CLOEXEC);
if (ctx->result == INVALID_SOCKET && accept4_works == -1) { if (ctx->result == INVALID_SOCKET && accept4_works == -1) {
/* On Linux older than 2.6.28, accept4() fails with ENOSYS */ /* On Linux older than 2.6.28, accept4() fails with ENOSYS */
@ -2231,9 +2325,9 @@ sock_accept_impl(PySocketSockObject *s, void *data)
} }
} }
if (accept4_works == 0) if (accept4_works == 0)
ctx->result = accept(s->sock_fd, SAS2SA(ctx->addrbuf), ctx->addrlen); ctx->result = accept(s->sock_fd, addr, paddrlen);
#else #else
ctx->result = accept(s->sock_fd, SAS2SA(ctx->addrbuf), ctx->addrlen); ctx->result = accept(s->sock_fd, addr, paddrlen);
#endif #endif
#ifdef MS_WINDOWS #ifdef MS_WINDOWS
@ -2435,9 +2529,12 @@ operations. A timeout of None indicates that timeouts on socket \n\
operations are disabled."); operations are disabled.");
/* s.setsockopt() method. /* 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; 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 * static PyObject *
sock_setsockopt(PySocketSockObject *s, PyObject *args) sock_setsockopt(PySocketSockObject *s, PyObject *args)
@ -2447,32 +2544,49 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args)
int res; int res;
Py_buffer optval; Py_buffer optval;
int flag; int flag;
unsigned int optlen;
PyObject *none;
/* setsockopt(level, opt, flag) */
if (PyArg_ParseTuple(args, "iii:setsockopt", if (PyArg_ParseTuple(args, "iii:setsockopt",
&level, &optname, &flag)) { &level, &optname, &flag)) {
res = setsockopt(s->sock_fd, level, optname, res = setsockopt(s->sock_fd, level, optname,
(char*)&flag, sizeof flag); (char*)&flag, sizeof flag);
goto done;
} }
else {
PyErr_Clear(); PyErr_Clear();
if (!PyArg_ParseTuple(args, "iiy*:setsockopt", /* setsockopt(level, opt, (None, flag)) */
&level, &optname, &optval)) if (PyArg_ParseTuple(args, "iiO!I:setsockopt",
return NULL; &level, &optname, Py_TYPE(Py_None), &none, &optlen)) {
#ifdef MS_WINDOWS assert(sizeof(socklen_t) >= sizeof(unsigned int));
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, res = setsockopt(s->sock_fd, level, optname,
optval.buf, (int)optval.len); NULL, (socklen_t)optlen);
#else goto done;
res = setsockopt(s->sock_fd, level, optname, optval.buf, optval.len);
#endif
PyBuffer_Release(&optval);
} }
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);
#else
res = setsockopt(s->sock_fd, level, optname, optval.buf, optval.len);
#endif
PyBuffer_Release(&optval);
done:
if (res < 0) { if (res < 0) {
return s->errorhandler(); return s->errorhandler();
} }
@ -2481,10 +2595,13 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args)
} }
PyDoc_STRVAR(setsockopt_doc, 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\ \n\
Set a socket option. See the Unix manual for level and option.\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. /* s.getsockopt() method.
@ -3773,6 +3890,51 @@ struct sock_sendmsg {
ssize_t result; ssize_t result;
}; };
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 static int
sock_sendmsg_impl(PySocketSockObject *s, void *data) sock_sendmsg_impl(PySocketSockObject *s, void *data)
{ {
@ -3787,9 +3949,8 @@ sock_sendmsg_impl(PySocketSockObject *s, void *data)
static PyObject * static PyObject *
sock_sendmsg(PySocketSockObject *s, PyObject *args) 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; Py_buffer *databufs = NULL;
struct iovec *iovs = NULL;
sock_addr_t addrbuf; sock_addr_t addrbuf;
struct msghdr msg = {0}; struct msghdr msg = {0};
struct cmsginfo { struct cmsginfo {
@ -3800,7 +3961,7 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
void *controlbuf = NULL; void *controlbuf = NULL;
size_t controllen, controllen_last; size_t controllen, controllen_last;
int addrlen, flags = 0; 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; *cmsg_fast = NULL, *retval = NULL;
struct sock_sendmsg ctx; struct sock_sendmsg ctx;
@ -3818,30 +3979,8 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args)
/* Fill in an iovec for each message part, and save the Py_buffer /* Fill in an iovec for each message part, and save the Py_buffer
structs to release afterwards. */ structs to release afterwards. */
if ((data_fast = PySequence_Fast(data_arg, if (sock_sendmsg_iovec(s, data_arg, &msg, &databufs, &ndatabufs) == -1) {
"sendmsg() argument 1 must be an "
"iterable")) == NULL)
goto finally; 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) if (cmsg_arg == NULL)
@ -3972,8 +4111,6 @@ finally:
for (i = 0; i < ndatabufs; i++) for (i = 0; i < ndatabufs; i++)
PyBuffer_Release(&databufs[i]); PyBuffer_Release(&databufs[i]);
PyMem_Free(databufs); PyMem_Free(databufs);
PyMem_Free(iovs);
Py_XDECREF(data_fast);
return retval; return retval;
} }
@ -3995,6 +4132,165 @@ the message. The return value is the number of bytes of non-ancillary\n\
data sent."); data sent.");
#endif /* CMSG_LEN */ #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 */ /* s.shutdown(how) method */
@ -4173,6 +4469,10 @@ static PyMethodDef sock_methods[] = {
recvmsg_into_doc,}, recvmsg_into_doc,},
{"sendmsg", (PyCFunction)sock_sendmsg, METH_VARARGS, {"sendmsg", (PyCFunction)sock_sendmsg, METH_VARARGS,
sendmsg_doc}, sendmsg_doc},
#endif
#ifdef HAVE_SOCKADDR_ALG
{"sendmsg_afalg", (PyCFunction)sock_sendmsg_afalg, METH_VARARGS | METH_KEYWORDS,
sendmsg_afalg_doc},
#endif #endif
{NULL, NULL} /* sentinel */ {NULL, NULL} /* sentinel */
}; };
@ -6277,6 +6577,9 @@ PyInit__socket(void)
/* Reserved for Werner's ATM */ /* Reserved for Werner's ATM */
PyModule_AddIntMacro(m, AF_AAL5); PyModule_AddIntMacro(m, AF_AAL5);
#endif #endif
#ifdef HAVE_SOCKADDR_ALG
PyModule_AddIntMacro(m, AF_ALG); /* Linux crypto */
#endif
#ifdef AF_X25 #ifdef AF_X25
/* Reserved for X.25 project */ /* Reserved for X.25 project */
PyModule_AddIntMacro(m, AF_X25); PyModule_AddIntMacro(m, AF_X25);
@ -6338,6 +6641,9 @@ PyInit__socket(void)
#ifdef NETLINK_TAPBASE #ifdef NETLINK_TAPBASE
PyModule_AddIntMacro(m, NETLINK_TAPBASE); PyModule_AddIntMacro(m, NETLINK_TAPBASE);
#endif #endif
#ifdef NETLINK_CRYPTO
PyModule_AddIntMacro(m, NETLINK_CRYPTO);
#endif
#endif /* AF_NETLINK */ #endif /* AF_NETLINK */
#ifdef AF_ROUTE #ifdef AF_ROUTE
/* Alias to emulate 4.4BSD */ /* Alias to emulate 4.4BSD */
@ -6491,6 +6797,22 @@ PyInit__socket(void)
PyModule_AddIntMacro(m, TIPC_TOP_SRV); PyModule_AddIntMacro(m, TIPC_TOP_SRV);
#endif #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 */ /* Socket types */
PyModule_AddIntMacro(m, SOCK_STREAM); PyModule_AddIntMacro(m, SOCK_STREAM);
PyModule_AddIntMacro(m, SOCK_DGRAM); PyModule_AddIntMacro(m, SOCK_DGRAM);
@ -6761,6 +7083,9 @@ PyInit__socket(void)
#ifdef SOL_RDS #ifdef SOL_RDS
PyModule_AddIntMacro(m, SOL_RDS); PyModule_AddIntMacro(m, SOL_RDS);
#endif #endif
#ifdef HAVE_SOCKADDR_ALG
PyModule_AddIntMacro(m, SOL_ALG);
#endif
#ifdef RDS_CANCEL_SENT_TO #ifdef RDS_CANCEL_SENT_TO
PyModule_AddIntMacro(m, RDS_CANCEL_SENT_TO); PyModule_AddIntMacro(m, RDS_CANCEL_SENT_TO);
#endif #endif

49
configure vendored
View File

@ -775,7 +775,6 @@ infodir
docdir docdir
oldincludedir oldincludedir
includedir includedir
runstatedir
localstatedir localstatedir
sharedstatedir sharedstatedir
sysconfdir sysconfdir
@ -886,7 +885,6 @@ datadir='${datarootdir}'
sysconfdir='${prefix}/etc' sysconfdir='${prefix}/etc'
sharedstatedir='${prefix}/com' sharedstatedir='${prefix}/com'
localstatedir='${prefix}/var' localstatedir='${prefix}/var'
runstatedir='${localstatedir}/run'
includedir='${prefix}/include' includedir='${prefix}/include'
oldincludedir='/usr/include' oldincludedir='/usr/include'
docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' docdir='${datarootdir}/doc/${PACKAGE_TARNAME}'
@ -1139,15 +1137,6 @@ do
| -silent | --silent | --silen | --sile | --sil) | -silent | --silent | --silen | --sile | --sil)
silent=yes ;; 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) -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb)
ac_prev=sbindir ;; ac_prev=sbindir ;;
-sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \
@ -1285,7 +1274,7 @@ fi
for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \
datadir sysconfdir sharedstatedir localstatedir includedir \ datadir sysconfdir sharedstatedir localstatedir includedir \
oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ oldincludedir docdir infodir htmldir dvidir pdfdir psdir \
libdir localedir mandir runstatedir libdir localedir mandir
do do
eval ac_val=\$$ac_var eval ac_val=\$$ac_var
# Remove trailing slashes. # Remove trailing slashes.
@ -1438,7 +1427,6 @@ Fine tuning of the installation directories:
--sysconfdir=DIR read-only single-machine data [PREFIX/etc] --sysconfdir=DIR read-only single-machine data [PREFIX/etc]
--sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com]
--localstatedir=DIR modifiable single-machine data [PREFIX/var] --localstatedir=DIR modifiable single-machine data [PREFIX/var]
--runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run]
--libdir=DIR object code libraries [EPREFIX/lib] --libdir=DIR object code libraries [EPREFIX/lib]
--includedir=DIR C header files [PREFIX/include] --includedir=DIR C header files [PREFIX/include]
--oldincludedir=DIR C header files for non-gcc [/usr/include] --oldincludedir=DIR C header files for non-gcc [/usr/include]
@ -13062,6 +13050,41 @@ $as_echo "#define HAVE_SOCKADDR_STORAGE 1" >>confdefs.h
fi 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 # checks for compiler characteristics
{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether char is unsigned" >&5 { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether char is unsigned" >&5

View File

@ -3862,6 +3862,19 @@ if test $ac_cv_struct_sockaddr_storage = yes; then
AC_DEFINE(HAVE_SOCKADDR_STORAGE, 1, [struct sockaddr_storage (sys/socket.h)]) AC_DEFINE(HAVE_SOCKADDR_STORAGE, 1, [struct sockaddr_storage (sys/socket.h)])
fi 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 # checks for compiler characteristics
AC_C_CHAR_UNSIGNED AC_C_CHAR_UNSIGNED

View File

@ -892,6 +892,9 @@
/* Define to 1 if you have the `snprintf' function. */ /* Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF #undef HAVE_SNPRINTF
/* struct sockaddr_alg (linux/if_alg.h) */
#undef HAVE_SOCKADDR_ALG
/* Define if sockaddr has sa_len member */ /* Define if sockaddr has sa_len member */
#undef HAVE_SOCKADDR_SA_LEN #undef HAVE_SOCKADDR_SA_LEN