diff options
author | Christian Heimes <christian@python.org> | 2016-09-05 21:54:41 (GMT) |
---|---|---|
committer | Christian Heimes <christian@python.org> | 2016-09-05 21:54:41 (GMT) |
commit | dffa3949c7431e819c64a890bce41fe769e47da1 (patch) | |
tree | 76a4293c96ba8a839d90cda3e74e6dd91cce9274 /Modules/socketmodule.c | |
parent | 92a6c170e6897ee98c36a3a9087b1a7d3e054d2b (diff) | |
download | cpython-dffa3949c7431e819c64a890bce41fe769e47da1.zip cpython-dffa3949c7431e819c64a890bce41fe769e47da1.tar.gz cpython-dffa3949c7431e819c64a890bce41fe769e47da1.tar.bz2 |
Issue #27744: Add AF_ALG (Linux Kernel crypto) to socket module.
Diffstat (limited to 'Modules/socketmodule.c')
-rw-r--r-- | Modules/socketmodule.c | 431 |
1 files changed, 378 insertions, 53 deletions
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c index f94c322..42ae2fb 100644 --- a/Modules/socketmodule.c +++ b/Modules/socketmodule.c @@ -136,7 +136,7 @@ sendall(data[, flags]) -- send all data\n\ 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 @@ http://cvsweb.netbsd.org/bsdweb.cgi/src/lib/libc/net/getaddrinfo.c.diff?r1=1.82& #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 @@ makesockaddr(SOCKET_T sockfd, struct sockaddr *addr, size_t addrlen, int proto) } #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 @@ getsockaddrarg(PySocketSockObject *s, PyObject *args, 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 @@ getsockaddrlen(PySocketSockObject *s, socklen_t *len_ret) 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 @@ static int 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 @@ sock_accept_impl(PySocketSockObject *s, void *data) } } 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. A timeout of None indicates that timeouts on socket \n\ 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 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args) 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); + goto done; } - else { - PyErr_Clear(); - 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; - } + + 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, - optval.buf, (int)optval.len); -#else - res = setsockopt(s->sock_fd, level, optname, optval.buf, optval.len); -#endif + 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); +#else + 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 @@ sock_setsockopt(PySocketSockObject *s, PyObject *args) } 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 @@ struct sock_sendmsg { }; 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 @@ sock_sendmsg_impl(PySocketSockObject *s, void *data) 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 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args) 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,31 +3979,9 @@ sock_sendmsg(PySocketSockObject *s, PyObject *args) /* 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(); + if (sock_sendmsg_iovec(s, data_arg, &msg, &databufs, &ndatabufs) == -1) { 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) ncmsgs = 0; @@ -3972,8 +4111,6 @@ finally: 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 @@ the message. The return value is the number of bytes of non-ancillary\n\ 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 @@ static PyMethodDef sock_methods[] = { {"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 @@ PyInit__socket(void) /* 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 @@ PyInit__socket(void) #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 @@ PyInit__socket(void) 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 @@ PyInit__socket(void) #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 |