summaryrefslogtreecommitdiffstats
path: root/src/responder/nss/nss_protocol.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/responder/nss/nss_protocol.c')
-rw-r--r--src/responder/nss/nss_protocol.c487
1 files changed, 487 insertions, 0 deletions
diff --git a/src/responder/nss/nss_protocol.c b/src/responder/nss/nss_protocol.c
new file mode 100644
index 0000000..e6dc702
--- /dev/null
+++ b/src/responder/nss/nss_protocol.c
@@ -0,0 +1,487 @@
+/*
+ Authors:
+ Pavel Březina <pbrezina@redhat.com>
+
+ Copyright (C) 2016 Red Hat
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 3 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include "util/util.h"
+#include "util/cert.h"
+#include "lib/idmap/sss_idmap.h"
+#include "responder/nss/nss_protocol.h"
+#include <arpa/inet.h>
+
+errno_t
+sss_nss_protocol_done(struct cli_ctx *cli_ctx, errno_t error)
+{
+ struct cli_protocol *pctx;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ switch (error) {
+ case EOK:
+ /* Create empty packet if none was provided. */
+ if (pctx->creq->out == NULL) {
+ ret = sss_packet_new(pctx->creq, 0,
+ sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: success\n");
+ ret = EOK;
+ goto done;
+ case ENOENT:
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: not found\n");
+ ret = sss_cmd_send_empty(cli_ctx);
+ goto done;
+ default:
+ DEBUG(SSSDBG_TRACE_ALL, "Sending reply: error [%d]: %s\n",
+ error, sss_strerror(error));
+ ret = sss_cmd_send_error(cli_ctx, error);
+ goto done;
+ }
+
+done:
+ if (ret != EOK) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Unable to send reply [%d]: %s!\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ sss_cmd_done(cli_ctx, NULL);
+ return EOK;
+}
+
+void sss_nss_protocol_reply(struct cli_ctx *cli_ctx,
+ struct sss_nss_ctx *nss_ctx,
+ struct sss_nss_cmd_ctx *cmd_ctx,
+ struct cache_req_result *result,
+ sss_nss_protocol_fill_packet_fn fill_fn)
+{
+ struct cli_protocol *pctx;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ ret = sss_packet_new(pctx->creq, 0, sss_packet_get_cmd(pctx->creq->in),
+ &pctx->creq->out);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = fill_fn(nss_ctx, cmd_ctx, pctx->creq->out, result);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ sss_packet_set_error(pctx->creq->out, EOK);
+
+done:
+ sss_nss_protocol_done(cli_ctx, ret);
+}
+
+errno_t
+sss_nss_protocol_parse_name(struct cli_ctx *cli_ctx, const char **_rawname)
+{
+ struct cli_protocol *pctx;
+ const char *rawname;
+ uint8_t *body;
+ size_t blen;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated!\n");
+ return EINVAL;
+ }
+
+ /* If the body isn't valid UTF-8, fail */
+ if (!sss_utf8_check(body, blen - 1)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not UTF-8 string!\n");
+ return EINVAL;
+ }
+
+ rawname = (const char *)body;
+ if (rawname[0] == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "An empty name was provided!\n");
+ return EINVAL;
+ }
+
+ *_rawname = rawname;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_name_ex(struct cli_ctx *cli_ctx, const char **_rawname,
+ uint32_t *_flags)
+{
+ struct cli_protocol *pctx;
+ const char *rawname;
+ uint8_t *body;
+ size_t blen;
+ uint8_t *p;
+ uint32_t flags;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ if (blen < 1 + sizeof(uint32_t)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body too short!\n");
+ return EINVAL;
+ }
+
+ /* If first argument not terminated fail. */
+ if (body[blen - 1 - sizeof(uint32_t)] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated!\n");
+ return EINVAL;
+ }
+
+ p = memchr(body, '\0', blen);
+ /* Although body for sure is null terminated, let's add this check here
+ * so static analyzers are happier. */
+ if (p == NULL) {
+ DEBUG(SSSDBG_CRIT_FAILURE,
+ "memchr() returned NULL, body is not null terminated!\n");
+ return EINVAL;
+ }
+
+ /* If the body isn't valid UTF-8, fail */
+ if (!sss_utf8_check(body, (p - body))) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "First argument is not UTF-8 string!\n");
+ return EINVAL;
+ }
+
+ rawname = (const char *)body;
+ if (rawname[0] == '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "An empty name was provided!\n");
+ return EINVAL;
+ }
+
+ p++;
+ if ((p - body) + sizeof(uint32_t) != blen) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body has unexpected size!\n");
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&flags, p, NULL);
+
+ *_rawname = rawname;
+ *_flags = flags;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_id(struct cli_ctx *cli_ctx, uint32_t *_id)
+{
+ struct cli_protocol *pctx;
+ uint8_t *body;
+ size_t blen;
+ uint32_t id;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ if (blen != sizeof(uint32_t)) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&id, body, NULL);
+
+ *_id = id;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_id_ex(struct cli_ctx *cli_ctx, uint32_t *_id,
+ uint32_t *_flags)
+{
+ struct cli_protocol *pctx;
+ uint8_t *body;
+ size_t blen;
+ uint32_t id;
+ uint32_t flags;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ if (blen != 2 * sizeof(uint32_t)) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&id, body, NULL);
+ SAFEALIGN_COPY_UINT32(&flags, body + sizeof(uint32_t), NULL);
+
+ *_id = id;
+ *_flags = flags;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_limit(struct cli_ctx *cli_ctx, uint32_t *_limit)
+{
+ return sss_nss_protocol_parse_id(cli_ctx, _limit);
+}
+
+errno_t
+sss_nss_protocol_parse_svc_name(struct cli_ctx *cli_ctx,
+ const char **_name,
+ const char **_protocol)
+{
+ struct cli_protocol *pctx;
+ const char *protocol;
+ const char *name;
+ size_t protocol_len;
+ size_t name_len;
+ uint8_t *body;
+ size_t blen;
+ int i;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated\n");
+ return EINVAL;
+ }
+
+ /* Calculate service name length. */
+ for (i = 0, name_len = 0; body[i] != '\0'; i++) {
+ name_len++;
+ }
+
+ /* Calculate protocol name length, use index from previous cycle. */
+ for (protocol_len = 0; body[i + 1] != '\0'; i++) {
+ protocol_len++;
+ }
+
+ if (name_len == 0) {
+ return EINVAL;
+ }
+
+ name = (const char *)body;
+ protocol = protocol_len == 0 ? NULL : (const char *)(body + name_len + 1);
+
+ if (!sss_utf8_check((const uint8_t *)name, name_len)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Name is not UTF-8 string\n");
+ return EINVAL;
+ }
+
+ if (!sss_utf8_check((const uint8_t *)protocol, protocol_len)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Protocol is not UTF-8 string\n");
+ return EINVAL;
+ }
+
+ *_name = name;
+ *_protocol = protocol;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_svc_port(struct cli_ctx *cli_ctx,
+ uint16_t *_port,
+ const char **_protocol)
+{
+ struct cli_protocol *pctx;
+ const char *protocol;
+ size_t protocol_len;
+ uint16_t port;
+ uint8_t *body;
+ size_t blen;
+ int i;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated\n");
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT16(&port, body, NULL);
+ port = ntohs(port);
+
+ /* Move behind the port and padding to get the protocol. */
+ body = body + 2 * sizeof(uint16_t) + sizeof(uint32_t);
+
+ /* Calculate protocol name length. */
+ for (protocol_len = 0, i = 0; body[i] != '\0'; i++) {
+ protocol_len++;
+ }
+
+ protocol = protocol_len == 0 ? NULL : (const char *)body;
+
+ if (!sss_utf8_check((const uint8_t *)protocol, protocol_len)) {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Protocol is not UTF-8 string\n");
+ return EINVAL;
+ }
+
+ *_port = port;
+ *_protocol = protocol;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_cert(struct cli_ctx *cli_ctx,
+ const char **_derb64)
+{
+ struct cli_protocol *pctx;
+ const char *derb64;
+ size_t pem_size;
+ char *pem_cert;
+ uint8_t *body;
+ size_t blen;
+ errno_t ret;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated\n");
+ return EINVAL;
+ }
+
+ derb64 = (const char *)body;
+
+ DEBUG(SSSDBG_TRACE_ALL, "Input certificate [%s]\n", derb64);
+
+ /* Check input. */
+ ret = sss_cert_derb64_to_pem(cli_ctx, derb64, &pem_cert, &pem_size);
+ if (ret != EOK) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to convert certificate to pem [%d]: %s\n",
+ ret, sss_strerror(ret));
+ return ret;
+ }
+
+ talloc_free(pem_cert);
+
+ *_derb64 = derb64;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_sid(struct cli_ctx *cli_ctx,
+ const char **_sid)
+{
+ struct cli_protocol *pctx;
+ struct sss_nss_ctx *nss_ctx;
+ const char *sid;
+ uint8_t *bin_sid;
+ size_t bin_len;
+ uint8_t *body;
+ size_t blen;
+ enum idmap_error_code err;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+ nss_ctx = talloc_get_type(cli_ctx->rctx->pvt_ctx, struct sss_nss_ctx);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ /* If not terminated fail. */
+ if (body[blen - 1] != '\0') {
+ DEBUG(SSSDBG_CRIT_FAILURE, "Body is not null terminated\n");
+ return EINVAL;
+ }
+
+ sid = (const char *)body;
+
+ /* If the body isn't a SID, fail */
+ err = sss_idmap_sid_to_bin_sid(nss_ctx->idmap_ctx, sid, &bin_sid,
+ &bin_len);
+ if (err != IDMAP_SUCCESS) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Unable to convert SID to binary [%s].\n", sid);
+ return EINVAL;
+ }
+
+ sss_idmap_free_bin_sid(nss_ctx->idmap_ctx, bin_sid);
+
+ DEBUG(SSSDBG_TRACE_ALL, "Input SID [%s]\n", sid);
+
+ *_sid = sid;
+
+ return EOK;
+}
+
+errno_t
+sss_nss_protocol_parse_addr(struct cli_ctx *cli_ctx,
+ uint32_t *_af,
+ uint32_t *_addrlen,
+ uint8_t **_addr)
+{
+ struct cli_protocol *pctx;
+ uint8_t *body;
+ size_t blen;
+ uint32_t af;
+ uint8_t *addr;
+ socklen_t addrlen;
+ char buf[INET6_ADDRSTRLEN];
+ const char *addrstr = NULL;
+
+ pctx = talloc_get_type(cli_ctx->protocol_ctx, struct cli_protocol);
+
+ sss_packet_get_body(pctx->creq->in, &body, &blen);
+
+ if (blen < sizeof(uint32_t) * 2) {
+ return EINVAL;
+ }
+
+ SAFEALIGN_COPY_UINT32(&af, body, NULL);
+ SAFEALIGN_COPY_UINT32(&addrlen, body + sizeof(uint32_t), NULL);
+
+ addr = body + sizeof(uint32_t) * 2;
+
+ /* If the body isn't a addr, fail */
+ addrstr = inet_ntop(af, addr, buf, INET6_ADDRSTRLEN);
+ if (addrstr == NULL) {
+ DEBUG(SSSDBG_OP_FAILURE,
+ "Failed to parse address: %s\n", strerror(errno));
+ return EINVAL;
+ }
+
+ DEBUG(SSSDBG_TRACE_ALL, "Input address [%s]\n", addrstr);
+
+ *_af = af;
+ *_addr = addr;
+ *_addrlen = addrlen;
+
+ return EOK;
+}