LCOV - code coverage report
Current view: top level - util - become_user.c (source / functions) Hit Total Coverage
Test: .coverage.total Lines: 50 83 60.2 %
Date: 2015-10-19 Functions: 3 3 100.0 %

          Line data    Source code
       1             : /*
       2             :     SSSD
       3             : 
       4             :     Kerberos 5 Backend Module -- Utilities
       5             : 
       6             :     Authors:
       7             :         Sumit Bose <sbose@redhat.com>
       8             : 
       9             :     Copyright (C) 2009 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             : #include "util/util.h"
      26             : #include <grp.h>
      27             : 
      28           5 : errno_t become_user(uid_t uid, gid_t gid)
      29             : {
      30             :     uid_t cuid;
      31             :     int ret;
      32             : 
      33           5 :     DEBUG(SSSDBG_FUNC_DATA,
      34             :           "Trying to become user [%"SPRIuid"][%"SPRIgid"].\n", uid, gid);
      35             : 
      36             :     /* skip call if we already are the requested user */
      37           5 :     cuid = geteuid();
      38           5 :     if (uid == cuid) {
      39           3 :         DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
      40           3 :         return EOK;
      41             :     }
      42             : 
      43             :     /* drop supplmentary groups first */
      44           2 :     ret = setgroups(0, NULL);
      45           2 :     if (ret == -1) {
      46           0 :         ret = errno;
      47           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
      48             :               "setgroups failed [%d][%s].\n", ret, strerror(ret));
      49           0 :         return ret;
      50             :     }
      51             : 
      52             :     /* change gid so that root cannot be regained (changes saved gid too) */
      53           2 :     ret = setresgid(gid, gid, gid);
      54           2 :     if (ret == -1) {
      55           0 :         ret = errno;
      56           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
      57             :               "setresgid failed [%d][%s].\n", ret, strerror(ret));
      58           0 :         return ret;
      59             :     }
      60             : 
      61             :     /* change uid so that root cannot be regained (changes saved uid too) */
      62             :     /* this call also takes care of dropping CAP_SETUID, so this is a PNR */
      63           2 :     ret = setresuid(uid, uid, uid);
      64           2 :     if (ret == -1) {
      65           0 :         ret = errno;
      66           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
      67             :               "setresuid failed [%d][%s].\n", ret, strerror(ret));
      68           0 :         return ret;
      69             :     }
      70             : 
      71           2 :     return EOK;
      72             : }
      73             : 
      74             : struct sss_creds {
      75             :     uid_t uid;
      76             :     gid_t gid;
      77             :     int num_gids;
      78             :     gid_t gids[];
      79             : };
      80             : 
      81             : errno_t restore_creds(struct sss_creds *saved_creds);
      82             : 
      83             : /* This is a reversible version of become_user, and returns the saved
      84             :  * credentials so that creds can be switched back calling restore_creds */
      85           3 : errno_t switch_creds(TALLOC_CTX *mem_ctx,
      86             :                      uid_t uid, gid_t gid,
      87             :                      int num_gids, gid_t *gids,
      88             :                      struct sss_creds **saved_creds)
      89             : {
      90           3 :     struct sss_creds *ssc = NULL;
      91             :     int size;
      92             :     int ret;
      93             :     uid_t myuid;
      94             :     uid_t mygid;
      95             : 
      96           3 :     DEBUG(SSSDBG_FUNC_DATA, "Switch user to [%d][%d].\n", uid, gid);
      97             : 
      98           3 :     myuid = geteuid();
      99           3 :     mygid = getegid();
     100             : 
     101           3 :     if (saved_creds) {
     102             :         /* save current user credentials */
     103           2 :         size = getgroups(0, NULL);
     104           2 :         if (size == -1) {
     105           0 :             ret = errno;
     106           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
     107             :                                         ret, strerror(ret));
     108           0 :             goto done;
     109             :         }
     110             : 
     111           2 :         ssc = talloc_size(mem_ctx,
     112             :                           (sizeof(struct sss_creds) + size * sizeof(gid_t)));
     113           2 :         if (!ssc) {
     114           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Allocation failed!\n");
     115           0 :             ret = ENOMEM;
     116           0 :             goto done;
     117             :         }
     118           2 :         ssc->num_gids = size;
     119             : 
     120           2 :         size = getgroups(ssc->num_gids, ssc->gids);
     121           2 :         if (size == -1) {
     122           0 :             ret = errno;
     123           0 :             DEBUG(SSSDBG_CRIT_FAILURE, "Getgroups failed! (%d, %s)\n",
     124             :                                         ret, strerror(ret));
     125             :             /* free ssc immediately otherwise the code will try to restore
     126             :              * wrong creds */
     127           0 :             talloc_zfree(ssc);
     128           0 :             goto done;
     129             :         }
     130             : 
     131             :         /* we care only about effective ids */
     132           2 :         ssc->uid = myuid;
     133           2 :         ssc->gid = mygid;
     134             :     }
     135             : 
     136             :     /* if we are regaining root set euid first so that we have CAP_SETUID back,
     137             :      * ane the other calls work too, otherwise call it last so that we can
     138             :      * change groups before we loose CAP_SETUID */
     139           3 :     if (uid == 0) {
     140           1 :         ret = setresuid(0, 0, 0);
     141           1 :         if (ret == -1) {
     142           0 :             ret = errno;
     143           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     144             :                   "setresuid failed [%d][%s].\n", ret, strerror(ret));
     145           0 :             goto done;
     146             :         }
     147             :     }
     148             : 
     149             :     /* TODO: use libcap-ng if we need to get/set capabilities too ? */
     150             : 
     151           3 :     if (myuid == uid && mygid == gid) {
     152           1 :         DEBUG(SSSDBG_FUNC_DATA, "Already user [%"SPRIuid"].\n", uid);
     153           1 :         return EOK;
     154             :     }
     155             : 
     156             :     /* try to setgroups first should always work if CAP_SETUID is set,
     157             :      * otherwise it will always fail, failure is not critical though as
     158             :      * generally we only really care about uid and at mot primary gid */
     159           2 :     ret = setgroups(num_gids, gids);
     160           2 :     if (ret == -1) {
     161           0 :         ret = errno;
     162           0 :         DEBUG(SSSDBG_TRACE_FUNC,
     163             :               "setgroups failed [%d][%s].\n", ret, strerror(ret));
     164             :     }
     165             : 
     166             :     /* change gid now, (leaves saved gid to current, so we can restore) */
     167           2 :     ret = setresgid(-1, gid, -1);
     168           2 :     if (ret == -1) {
     169           0 :         ret = errno;
     170           0 :         DEBUG(SSSDBG_CRIT_FAILURE,
     171             :               "setresgid failed [%d][%s].\n", ret, strerror(ret));
     172           0 :         goto done;
     173             :     }
     174             : 
     175           2 :     if (uid != 0) {
     176             :         /* change uid, (leaves saved uid to current, so we can restore) */
     177           1 :         ret = setresuid(-1, uid, -1);
     178           1 :         if (ret == -1) {
     179           0 :             ret = errno;
     180           0 :             DEBUG(SSSDBG_CRIT_FAILURE,
     181             :                   "setresuid failed [%d][%s].\n", ret, strerror(ret));
     182           0 :             goto done;
     183             :         }
     184             :     }
     185             : 
     186           2 :     ret = 0;
     187             : 
     188             : done:
     189           2 :     if (ret) {
     190             :         /* attempt to restore creds first */
     191           0 :         restore_creds(ssc);
     192           0 :         talloc_free(ssc);
     193           2 :     } else if (saved_creds) {
     194           1 :         *saved_creds = ssc;
     195             :     }
     196           2 :     return ret;
     197             : }
     198             : 
     199           1 : errno_t restore_creds(struct sss_creds *saved_creds)
     200             : {
     201           1 :     if (saved_creds == NULL) {
     202             :         /* In case save_creds was saved with the UID already dropped */
     203           0 :         return EOK;
     204             :     }
     205             : 
     206           1 :     return switch_creds(saved_creds,
     207             :                         saved_creds->uid,
     208             :                         saved_creds->gid,
     209             :                         saved_creds->num_gids,
     210           1 :                         saved_creds->gids, NULL);
     211             : }

Generated by: LCOV version 1.10