b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek KCM Server - the KCM server request and reply parsing and dispatching
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek Copyright (C) Red Hat, 2016
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek This program is free software; you can redistribute it and/or modify
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek it under the terms of the GNU General Public License as published by
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek the Free Software Foundation; either version 3 of the License, or
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek (at your option) any later version.
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek This program is distributed in the hope that it will be useful,
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek but WITHOUT ANY WARRANTY; without even the implied warranty of
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek GNU General Public License for more details.
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek You should have received a copy of the GNU General Public License
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek along with this program. If not, see <http://www.gnu.org/licenses/>.
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek/* The first four bytes of a message is always the size */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek/* The return code is 32bits */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek/* The maximum length of a request or reply as defined by the RPC
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * protocol. This is the same constant size as MIT KRB5 uses
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek/* KCM operation, its raw input and raw output and result */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * KCM IO-vector operations
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* We don't use iovec b/c void pointers don't allow for
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * pointer arithmetics and it's convenient to keep track
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * of processed bytes
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozekstatic errno_t kcm_iovec_op(int fd, struct kcm_iovec *kiov, bool do_read)
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek iov[0].iov_base = kiov->kiov_base + kiov->nprocessed;
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek iov[0].iov_len = kiov->kiov_len - kiov->nprocessed;
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* This iovec is full (read) or depleted (write), proceed to the next one */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek if (errno == EAGAIN || errno == EWOULDBLOCK || errno == EINTR) {
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* Read event on fd that doesn't yield data? error */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* Decrease the amount of available free space in the iovec */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozekstatic errno_t kcm_read_iovec(int fd, struct kcm_iovec *kiov)
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozekstatic errno_t kcm_write_iovec(int fd, struct kcm_iovec *kiov)
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * Parsing KCM input
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * The request is received as two IO vectors:
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * first iovec:
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * length 32-bit big-endian integer
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * second iovec:
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * major protocol number 8-bit big-endian integer
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * minor protocol number 8-bit big-endian integer
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * opcode 16-bit big-endian integer
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * message payload buffer
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* Includes the major, minor versions etc */
2f11cf256a10ca6f6ace35a05cc2edb46689567fFabiano Fidênciostatic uint32_t kcm_input_get_payload_len(struct kcm_iovec *v)
2f11cf256a10ca6f6ace35a05cc2edb46689567fFabiano Fidêncio /* The first 4 bytes before the payload is message length */
2f11cf256a10ca6f6ace35a05cc2edb46689567fFabiano Fidêncio SAFEALIGN_COPY_UINT32_CHECK(&len_be, v->kiov_base, v->kiov_len, &lc);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozekstatic errno_t kcm_input_parse(struct kcm_reqbuf *reqbuf,
2f11cf256a10ca6f6ace35a05cc2edb46689567fFabiano Fidêncio msglen = kcm_input_get_payload_len(&reqbuf->v_len);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek "Received message with length %"PRIu32"\n", msglen);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Illegal zero-length message\n");
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek "Sender claims the message is %"PRIu32" bytes, "
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek "but received %zu\n",
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek /* First 16 bits are 8 bit major and 8bit minor protocol version */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek KCM_PROTOCOL_VERSION_MAJOR, (uint16_t) proto_maj);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek KCM_PROTOCOL_VERSION_MINOR, (uint16_t) proto_maj);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "Did not find a KCM operation handler for the requested opcode\n");
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek /* The operation only receives the payload, not the opcode or the protocol info */
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek op_io->request.data = reqbuf->v_msg.kiov_base + mc;
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek op_io->request.length = reqbuf->v_msg.nprocessed - mc;
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * Constructing a reply for failure and success
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * The reply consists of three IO vectors:
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * 1) length iovec:
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * length: 32-bit big-endian
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * 2) return code iovec:
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * retcode: 32-bit big-endian. Non-zero on failure in the KCM server,
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * zero if the KCM operation ran (even if the operation itself
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * 3) reply iovec
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * message: buffer, first 32-bits of the buffer is the return code of
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * the KCM operation, the rest depends on the operation itself.
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * The buffer's length is specified by the first integer in the
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * reply (very intuitive, right?)
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * The client always reads the length and return code iovectors. However, the
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * client reads the reply iovec only if retcode is 0 in the return code iovector
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * (see kcmio_unix_socket_read() in the MIT tree)
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozekstatic errno_t kcm_failbuf_construct(errno_t ret,
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, 0, &c);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek SAFEALIGN_SETMEM_UINT32(repbuf->rcbuf, htobe32(ret), &c);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek DEBUG(SSSDBG_TRACE_LIBS, "Sent reply with error %d\n", ret);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek/* retcode is 0 if the operation at least ran, non-zero if there
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek * was some kind of internal KCM error, like input couldn't be parsed
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidênciostatic errno_t kcm_output_construct(TALLOC_CTX *mem_ctx,
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "Reply exceeds the KCM protocol limit, aborting\n");
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "Sending a reply with %zu bytes of payload\n", replen);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek SAFEALIGN_SETMEM_UINT32(repbuf->lenbuf, htobe32(replen), &c);
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio rep = talloc_zero_array(mem_ctx, uint8_t, replen);
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio "Failed to allocate memory for the message\n");
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio /* Set the buffer and its length to send to KCM client */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * Construct a reply buffer and send it to the KCM client
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozekstatic void kcm_reply_error(struct cli_ctx *cctx,
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek "KCM operation returs failure [%d]: %s\n",
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "Cannot construct the reply buffer, terminating client\n");
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek * Request-reply dispatcher
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* client context owns per-client buffers including this one */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* raw IO buffers */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* long-lived responder structures */
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidênciostatic void kcm_send_reply(struct kcm_req_ctx *req_ctx)
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio DEBUG(SSSDBG_TRACE_INTERNAL, "Sending a reply\n");
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio ret = kcm_output_construct(cctx, &req_ctx->op_io, &req_ctx->repbuf);
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio "Cannot construct the reply buffer, terminating client\n");
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio kcm_reply_error(cctx, ret, &req_ctx->repbuf);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozekstatic void kcm_cmd_request_done(struct tevent_req *req);
2b5518eeaacc6245cfa77ee4a7086f16208060fcJakub Hrozekstatic errno_t kcm_cmd_dispatch(struct kcm_ctx *kctx,
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek DEBUG(SSSDBG_CRIT_FAILURE, "Failed to schedule KCM operation.\n");
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek tevent_req_set_callback(req, kcm_cmd_request_done, req_ctx);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozekstatic void kcm_cmd_request_done(struct tevent_req *req)
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek req_ctx = tevent_req_callback_data(req, struct kcm_req_ctx);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "KCM operation failed [%d]: %s\n", ret, sss_strerror(ret));
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio kcm_reply_error(req_ctx->cctx, ret, &req_ctx->repbuf);
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidênciostatic errno_t kcm_recv_data(TALLOC_CTX *mem_ctx,
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek /* Not all errors are fatal, hence we don't print DEBUG messages
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek * here, but in the caller
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio msglen = kcm_input_get_payload_len(&reqbuf->v_len);
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio "Request exceeds the KCM protocol limit, aborting\n");
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio msg = talloc_zero_array(mem_ctx, uint8_t, msglen);
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio "Failed to allocate memory for the message\n");
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio /* Set the buffer and its expected len to receive the data */
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek /* Not all errors are fatal, hence we don't print DEBUG messages
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek * here, but in the caller
bfc6d9d611bfbc54b3f738084d86fb887c8769b5Fabiano Fidêncio/* Mind that kcm_new_req() does not take a mem_ctx argument on purpose as we
bfc6d9d611bfbc54b3f738084d86fb887c8769b5Fabiano Fidêncio * really want the cctx to be the memory context here so that if the client
bfc6d9d611bfbc54b3f738084d86fb887c8769b5Fabiano Fidêncio * disconnects, the request goes away. */
bfc6d9d611bfbc54b3f738084d86fb887c8769b5Fabiano Fidênciostatic struct kcm_req_ctx *kcm_new_req(struct cli_ctx *cctx,
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek req->reqbuf.v_len.kiov_base = req->reqbuf.lenbuf;
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek req->repbuf.v_len.kiov_base = req->repbuf.lenbuf;
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek req->repbuf.v_rc.kiov_base = req->repbuf.rcbuf;
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek kctx = talloc_get_type(cctx->rctx->pvt_ctx, struct kcm_ctx);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx);
bfc6d9d611bfbc54b3f738084d86fb887c8769b5Fabiano Fidêncio /* A new request comes in, setup data structures. */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek "Cannot set up client connection\n");
786c40023e1348e7613805446ae821af7030b5d3Fabiano Fidêncio ret = kcm_recv_data(req, cctx->cfd, &req->reqbuf);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek DEBUG(SSSDBG_TRACE_ALL, "Client closed connection.\n");
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* all fine */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek "Failed to receive data (%d, %s), aborting client\n",
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek ret = kcm_input_parse(&req->reqbuf, &req->op_io);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek "Failed to parse data (%d, %s), aborting client\n",
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* do not read anymore, client is done sending */
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "Failed to dispatch KCM operation [%d]: %s\n",
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek /* Dispatched request resumes in kcm_cmd_request_done */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek /* Fail with reply */
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek req = talloc_get_type(cctx->state_ctx, struct kcm_req_ctx);
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_len);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "Failed to write the length iovec [%d]: %s\n",
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_rc);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "Failed to write the retcode iovec [%d]: %s\n",
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek ret = kcm_write_iovec(cctx->cfd, &req->repbuf.v_msg);
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek "Failed to write the msg iovec [%d]: %s\n",
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek DEBUG(SSSDBG_TRACE_ALL, "Sending data again..\n");
9dcdbf596e138df3eec202487549a67cd3b0091bJakub Hrozek DEBUG(SSSDBG_FATAL_FAILURE, "Failed to send data, aborting client!\n");
1ec4198f38d34a1f82a2db55d8c9782a434fb55fJakub Hrozek DEBUG(SSSDBG_TRACE_INTERNAL, "All data sent!\n");
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozekstatic void kcm_fd_handler(struct tevent_context *ev,
01ef93a43e1629006416f33111f6077b3e92b175Fabiano Fidêncio sss_client_fd_handler(ptr, kcm_recv, kcm_send, flags);
b9c563c29243291f40489bb0dcbf3946fca72d58Jakub Hrozek/* Dummy, not used here but required to link to other responder files */