LCOV - code coverage report
Current view: top level - providers/ipa - selinux_child.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 0 173 0.0 %
Date: 2015-10-19 Functions: 0 6 0.0 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     IPA back end -- set SELinux context in a child module
       5             : 
       6             :     Authors:
       7             :         Jakub Hrozek <jhrozek@redhat.com>
       8             : 
       9             :     Copyright (C) 2014 Red Hat
      10             : 
      11             :     This program is free software; you can redistribute it and/or modify
      12             :     it under the terms of the GNU General Public License as published by
      13             :     the Free Software Foundation; either version 3 of the License, or
      14             :     (at your option) any later version.
      15             : 
      16             :     This program is distributed in the hope that it will be useful,
      17             :     but WITHOUT ANY WARRANTY; without even the implied warranty of
      18             :     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      19             :     GNU General Public License for more details.
      20             : 
      21             :     You should have received a copy of the GNU General Public License
      22             :     along with this program.  If not, see <http://www.gnu.org/licenses/>.
      23             : */
      24             : 
      25             : 
      26             : #include <sys/types.h>
      27             : #include <unistd.h>
      28             : #include <sys/stat.h>
      29             : #include <popt.h>
      30             : 
      31             : #include "util/util.h"
      32             : #include "util/child_common.h"
      33             : #include "providers/dp_backend.h"
      34             : 
      35             : struct input_buffer {
      36             :     const char *seuser;
      37             :     const char *mls_range;
      38             :     const char *username;
      39             : };
      40             : 
      41           0 : static errno_t unpack_buffer(uint8_t *buf,
      42             :                              size_t size,
      43             :                              struct input_buffer *ibuf)
      44             : {
      45           0 :     size_t p = 0;
      46             :     uint32_t len;
      47             : 
      48             :     /* seuser */
      49           0 :     SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
      50           0 :     DEBUG(SSSDBG_TRACE_INTERNAL, "seuser length: %d\n", len);
      51           0 :     if (len == 0) {
      52           0 :         ibuf->seuser = "";
      53           0 :         DEBUG(SSSDBG_TRACE_INTERNAL,
      54             :               "Empty SELinux user, will delete the mapping\n");
      55             :     } else {
      56           0 :         if (len > size - p) return EINVAL;
      57           0 :         ibuf->seuser = talloc_strndup(ibuf, (char *)(buf + p), len);
      58           0 :         if (ibuf->seuser == NULL) return ENOMEM;
      59           0 :         DEBUG(SSSDBG_TRACE_INTERNAL, "seuser: %s\n", ibuf->seuser);
      60           0 :         p += len;
      61             :     }
      62             : 
      63             :     /* MLS range */
      64           0 :     SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
      65           0 :     DEBUG(SSSDBG_TRACE_INTERNAL, "mls_range length: %d\n", len);
      66           0 :     if (len == 0) {
      67           0 :         if (strcmp(ibuf->seuser, "") != 0) {
      68           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "No MLS mapping!\n");
      69           0 :             return EINVAL;
      70             :         }
      71             :     } else {
      72           0 :         if (len > size - p) return EINVAL;
      73           0 :         ibuf->mls_range = talloc_strndup(ibuf, (char *)(buf + p), len);
      74           0 :         if (ibuf->mls_range == NULL) return ENOMEM;
      75           0 :         DEBUG(SSSDBG_TRACE_INTERNAL, "mls_range: %s\n", ibuf->mls_range);
      76           0 :         p += len;
      77             :     }
      78             : 
      79             :     /* username */
      80           0 :     SAFEALIGN_COPY_UINT32_CHECK(&len, buf + p, size, &p);
      81           0 :     DEBUG(SSSDBG_TRACE_INTERNAL, "username length: %d\n", len);
      82           0 :     if (len == 0) {
      83           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "No username set!\n");
      84           0 :         return EINVAL;
      85             :     } else {
      86           0 :         if (len > size - p) return EINVAL;
      87           0 :         ibuf->username = talloc_strndup(ibuf, (char *)(buf + p), len);
      88           0 :         if (ibuf->username == NULL) return ENOMEM;
      89           0 :         DEBUG(SSSDBG_TRACE_INTERNAL, "username: %s\n", ibuf->username);
      90           0 :         p += len;
      91             :     }
      92             : 
      93           0 :     return EOK;
      94             : }
      95             : 
      96           0 : static errno_t pack_buffer(struct response *r, int result)
      97             : {
      98           0 :     size_t p = 0;
      99             : 
     100             :     /* A buffer with the following structure must be created:
     101             :      *   uint32_t status of the request (required)
     102             :      */
     103           0 :     r->size =  sizeof(uint32_t);
     104             : 
     105           0 :     r->buf = talloc_array(r, uint8_t, r->size);
     106           0 :     if(r->buf == NULL) {
     107           0 :         return ENOMEM;
     108             :     }
     109             : 
     110           0 :     DEBUG(SSSDBG_TRACE_FUNC, "result [%d]\n", result);
     111             : 
     112             :     /* result */
     113           0 :     SAFEALIGN_SET_UINT32(&r->buf[p], result, &p);
     114             : 
     115           0 :     return EOK;
     116             : }
     117             : 
     118           0 : static errno_t prepare_response(TALLOC_CTX *mem_ctx,
     119             :                                 int result,
     120             :                                 struct response **rsp)
     121             : {
     122             :     int ret;
     123           0 :     struct response *r = NULL;
     124             : 
     125           0 :     r = talloc_zero(mem_ctx, struct response);
     126           0 :     if (r == NULL) {
     127           0 :         return ENOMEM;
     128             :     }
     129             : 
     130           0 :     r->buf = NULL;
     131           0 :     r->size = 0;
     132             : 
     133           0 :     ret = pack_buffer(r, result);
     134           0 :     if (ret != EOK) {
     135           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "pack_buffer failed\n");
     136           0 :         return ret;
     137             :     }
     138             : 
     139           0 :     *rsp = r;
     140           0 :     DEBUG(SSSDBG_TRACE_ALL, "r->size: %zu\n", r->size);
     141           0 :     return EOK;
     142             : }
     143             : 
     144           0 : static int sc_set_seuser(const char *login_name, const char *seuser_name,
     145             :                          const char *mls)
     146             : {
     147             :     int ret;
     148             :     mode_t old_mask;
     149             : 
     150             :     /* This is a workaround for
     151             :      * https://bugzilla.redhat.com/show_bug.cgi?id=1186422 to make sure
     152             :      * the directories are created with the expected permissions
     153             :      */
     154           0 :     old_mask = umask(0);
     155           0 :     if (strcmp(seuser_name, "") == 0) {
     156             :         /* An empty SELinux user should cause SSSD to use the system
     157             :          * default. We need to remove the SELinux user from the DB
     158             :          * in that case
     159             :          */
     160           0 :         ret = del_seuser(login_name);
     161             :     } else {
     162           0 :         ret = set_seuser(login_name, seuser_name, mls);
     163             :     }
     164           0 :     umask(old_mask);
     165           0 :     return ret;
     166             : }
     167             : 
     168           0 : static bool seuser_needs_update(struct input_buffer *ibuf)
     169             : {
     170           0 :     bool needs_update = true;
     171           0 :     char *db_seuser = NULL;
     172           0 :     char *db_mls_range = NULL;
     173             :     errno_t ret;
     174             : 
     175           0 :     ret = get_seuser(ibuf, ibuf->username, &db_seuser, &db_mls_range);
     176           0 :     DEBUG(SSSDBG_TRACE_INTERNAL,
     177             :           "get_seuser: ret: %d seuser: %s mls: %s\n",
     178             :           ret, db_seuser ? db_seuser : "unknown",
     179             :           db_mls_range ? db_mls_range : "unknown");
     180           0 :     if (ret == EOK && db_seuser && db_mls_range &&
     181           0 :             strcmp(db_seuser, ibuf->seuser) == 0 &&
     182           0 :             strcmp(db_mls_range, ibuf->mls_range) == 0) {
     183           0 :         needs_update = false;
     184             :     }
     185             : 
     186           0 :     talloc_free(db_seuser);
     187           0 :     talloc_free(db_mls_range);
     188           0 :     return needs_update;
     189             : }
     190             : 
     191           0 : int main(int argc, const char *argv[])
     192             : {
     193             :     int opt;
     194             :     poptContext pc;
     195           0 :     int debug_fd = -1;
     196             :     errno_t ret;
     197           0 :     TALLOC_CTX *main_ctx = NULL;
     198           0 :     uint8_t *buf = NULL;
     199           0 :     ssize_t len = 0;
     200           0 :     struct input_buffer *ibuf = NULL;
     201           0 :     struct response *resp = NULL;
     202             :     ssize_t written;
     203             :     bool needs_update;
     204             : 
     205           0 :     struct poptOption long_options[] = {
     206             :         POPT_AUTOHELP
     207             :         {"debug-level", 'd', POPT_ARG_INT, &debug_level, 0,
     208           0 :          _("Debug level"), NULL},
     209             :         {"debug-timestamps", 0, POPT_ARG_INT, &debug_timestamps, 0,
     210           0 :          _("Add debug timestamps"), NULL},
     211             :         {"debug-microseconds", 0, POPT_ARG_INT, &debug_microseconds, 0,
     212           0 :          _("Show timestamps with microseconds"), NULL},
     213             :         {"debug-fd", 0, POPT_ARG_INT, &debug_fd, 0,
     214           0 :          _("An open file descriptor for the debug logs"), NULL},
     215             :         {"debug-to-stderr", 0, POPT_ARG_NONE | POPT_ARGFLAG_DOC_HIDDEN,
     216             :          &debug_to_stderr, 0,
     217           0 :          _("Send the debug output to stderr directly."), NULL },
     218             :         POPT_TABLEEND
     219             :     };
     220             : 
     221             :     /* Set debug level to invalid value so we can decide if -d 0 was used. */
     222           0 :     debug_level = SSSDBG_INVALID;
     223             : 
     224           0 :     pc = poptGetContext(argv[0], argc, argv, long_options, 0);
     225           0 :     while((opt = poptGetNextOpt(pc)) != -1) {
     226             :         switch(opt) {
     227             :         default:
     228           0 :         fprintf(stderr, "\nInvalid option %s: %s\n\n",
     229             :                   poptBadOption(pc, 0), poptStrerror(opt));
     230           0 :             poptPrintUsage(pc, stderr, 0);
     231           0 :             _exit(-1);
     232             :         }
     233             :     }
     234             : 
     235           0 :     poptFreeContext(pc);
     236             : 
     237           0 :     DEBUG_INIT(debug_level);
     238             : 
     239           0 :     debug_prg_name = talloc_asprintf(NULL, "[sssd[selinux_child[%d]]]", getpid());
     240           0 :     if (debug_prg_name == NULL) {
     241           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_asprintf failed.\n");
     242           0 :         goto fail;
     243             :     }
     244             : 
     245           0 :     if (debug_fd != -1) {
     246           0 :         ret = set_debug_file_from_fd(debug_fd);
     247           0 :         if (ret != EOK) {
     248           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "set_debug_file_from_fd failed.\n");
     249             :         }
     250             :     }
     251             : 
     252           0 :     DEBUG(SSSDBG_TRACE_FUNC, "selinux_child started.\n");
     253           0 :     DEBUG(SSSDBG_TRACE_INTERNAL,
     254             :           "Running with effective IDs: [%"SPRIuid"][%"SPRIgid"].\n",
     255             :           geteuid(), getegid());
     256             : 
     257             :     /* libsemanage calls access(2) which works with real IDs, not effective.
     258             :      * We need to switch also the real ID to 0.
     259             :      */
     260           0 :     if (getuid() != 0) {
     261           0 :         ret = setuid(0);
     262           0 :         if (ret == -1) {
     263           0 :             ret = errno;
     264           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     265             :                   "setuid failed: %d, selinux_child might not work!\n", ret);
     266             :         }
     267             :     }
     268             : 
     269           0 :     if (getgid() != 0) {
     270           0 :         ret = setgid(0);
     271           0 :         if (ret == -1) {
     272           0 :             ret = errno;
     273           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     274             :                   "setgid failed: %d, selinux_child might not work!\n", ret);
     275             :         }
     276             :     }
     277             : 
     278           0 :     DEBUG(SSSDBG_TRACE_INTERNAL,
     279             :           "Running with real IDs [%"SPRIuid"][%"SPRIgid"].\n",
     280             :           getuid(), getgid());
     281             : 
     282           0 :     main_ctx = talloc_new(NULL);
     283           0 :     if (main_ctx == NULL) {
     284           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_new failed.\n");
     285           0 :         talloc_free(discard_const(debug_prg_name));
     286           0 :         goto fail;
     287             :     }
     288           0 :     talloc_steal(main_ctx, debug_prg_name);
     289             : 
     290           0 :     buf = talloc_size(main_ctx, sizeof(uint8_t)*IN_BUF_SIZE);
     291           0 :     if (buf == NULL) {
     292           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_size failed.\n");
     293           0 :         goto fail;
     294             :     }
     295             : 
     296           0 :     ibuf = talloc_zero(main_ctx, struct input_buffer);
     297           0 :     if (ibuf == NULL) {
     298           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "talloc_zero failed.\n");
     299           0 :         goto fail;
     300             :     }
     301             : 
     302           0 :     DEBUG(SSSDBG_TRACE_FUNC, "context initialized\n");
     303             : 
     304           0 :     errno = 0;
     305           0 :     len = sss_atomic_read_s(STDIN_FILENO, buf, IN_BUF_SIZE);
     306           0 :     if (len == -1) {
     307           0 :         ret = errno;
     308           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "read failed [%d][%s].\n", ret, strerror(ret));
     309           0 :         goto fail;
     310             :     }
     311             : 
     312           0 :     close(STDIN_FILENO);
     313             : 
     314           0 :     ret = unpack_buffer(buf, len, ibuf);
     315           0 :     if (ret != EOK) {
     316           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     317             :               "unpack_buffer failed.[%d][%s].\n", ret, strerror(ret));
     318           0 :         goto fail;
     319             :     }
     320             : 
     321           0 :     DEBUG(SSSDBG_TRACE_FUNC, "performing selinux operations\n");
     322             : 
     323           0 :     needs_update = seuser_needs_update(ibuf);
     324           0 :     if (needs_update == true) {
     325           0 :         ret = sc_set_seuser(ibuf->username, ibuf->seuser, ibuf->mls_range);
     326           0 :         if (ret != EOK) {
     327           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Cannot set SELinux login context.\n");
     328           0 :             goto fail;
     329             :         }
     330             :     }
     331             : 
     332           0 :     ret = prepare_response(main_ctx, ret, &resp);
     333           0 :     if (ret != EOK) {
     334           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Failed to prepare response buffer.\n");
     335           0 :         goto fail;
     336             :     }
     337             : 
     338           0 :     errno = 0;
     339             : 
     340           0 :     written = sss_atomic_write_s(STDOUT_FILENO, resp->buf, resp->size);
     341           0 :     if (written == -1) {
     342           0 :         ret = errno;
     343           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "write failed [%d][%s].\n", ret,
     344             :                     strerror(ret));
     345           0 :         goto fail;
     346             :     }
     347             : 
     348           0 :     if (written != resp->size) {
     349           0 :         DEBUG(SSSDBG_CRIT_FAILURE, "Expected to write %zu bytes, wrote %zu\n",
     350             :               resp->size, written);
     351           0 :         goto fail;
     352             :     }
     353             : 
     354           0 :     DEBUG(SSSDBG_TRACE_FUNC, "selinux_child completed successfully\n");
     355           0 :     close(STDOUT_FILENO);
     356           0 :     talloc_free(main_ctx);
     357           0 :     return EXIT_SUCCESS;
     358             : fail:
     359           0 :     DEBUG(SSSDBG_CRIT_FAILURE, "selinux_child failed!\n");
     360           0 :     close(STDOUT_FILENO);
     361           0 :     talloc_free(main_ctx);
     362           0 :     return EXIT_FAILURE;
     363             : }

Generated by: LCOV version 1.10