LCOV - code coverage report
Current view: top level - source3/smbd - sec_ctx.c (source / functions) Hit Total Coverage
Test: coverage report for v4-17-test 1498b464 Lines: 136 179 76.0 %
Date: 2024-06-13 04:01:37 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /* 
       2             :    Unix SMB/CIFS implementation.
       3             :    uid/user handling
       4             :    Copyright (C) Tim Potter 2000
       5             : 
       6             :    This program is free software; you can redistribute it and/or modify
       7             :    it under the terms of the GNU General Public License as published by
       8             :    the Free Software Foundation; either version 3 of the License, or
       9             :    (at your option) any later version.
      10             : 
      11             :    This program is distributed in the hope that it will be useful,
      12             :    but WITHOUT ANY WARRANTY; without even the implied warranty of
      13             :    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
      14             :    GNU General Public License for more details.
      15             : 
      16             :    You should have received a copy of the GNU General Public License
      17             :    along with this program.  If not, see <http://www.gnu.org/licenses/>.
      18             : */
      19             : 
      20             : #include "includes.h"
      21             : #include "system/passwd.h"
      22             : #include "smbd/smbd.h"
      23             : #include "smbd/globals.h"
      24             : #include "libcli/security/security_token.h"
      25             : #include "auth.h"
      26             : #include "smbprofile.h"
      27             : #include "../lib/util/setid.h"
      28             : 
      29             : extern struct current_user current_user;
      30             : 
      31             : /****************************************************************************
      32             :  Are two UNIX tokens equal ?
      33             : ****************************************************************************/
      34             : 
      35         560 : bool unix_token_equal(const struct security_unix_token *t1, const struct security_unix_token *t2)
      36             : {
      37        1023 :         if (t1->uid != t2->uid || t1->gid != t2->gid ||
      38         555 :                         t1->ngroups != t2->ngroups) {
      39           5 :                 return false;
      40             :         }
      41         555 :         if (memcmp(t1->groups, t2->groups,
      42         555 :                         t1->ngroups*sizeof(gid_t)) != 0) {
      43           0 :                 return false;
      44             :         }
      45         555 :         return true;
      46             : }
      47             : 
      48             : /****************************************************************************
      49             :  Become the specified uid.
      50             : ****************************************************************************/
      51             : 
      52      221519 : static bool become_uid(uid_t uid)
      53             : {
      54             :         /* Check for dodgy uid values */
      55             : 
      56      221519 :         if (uid == (uid_t)-1 || 
      57             :             ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
      58           0 :                 if (!become_uid_done) {
      59           0 :                         DEBUG(1,("WARNING: using uid %d is a security risk\n",
      60             :                                  (int)uid));
      61           0 :                         become_uid_done = true;
      62             :                 }
      63             :         }
      64             : 
      65             :         /* Set effective user id */
      66             : 
      67      221519 :         set_effective_uid(uid);
      68             : 
      69      221519 :         return True;
      70             : }
      71             : 
      72             : /****************************************************************************
      73             :  Become the specified gid.
      74             : ****************************************************************************/
      75             : 
      76      221519 : static bool become_gid(gid_t gid)
      77             : {
      78             :         /* Check for dodgy gid values */
      79             : 
      80      221519 :         if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && 
      81             :                                  (gid == (gid_t)65535))) {
      82           0 :                 if (!become_gid_done) {
      83           0 :                         DEBUG(1,("WARNING: using gid %d is a security risk\n",
      84             :                                  (int)gid));  
      85           0 :                         become_gid_done = true;
      86             :                 }
      87             :         }
      88             : 
      89             :         /* Set effective group id */
      90             : 
      91      221519 :         set_effective_gid(gid);
      92      221519 :         return True;
      93             : }
      94             : 
      95             : /****************************************************************************
      96             :  Drop back to root privileges in order to change to another user.
      97             : ****************************************************************************/
      98             : 
      99      221519 : static void gain_root(void)
     100             : {
     101      221519 :         if (non_root_mode()) {
     102      221519 :                 return;
     103             :         }
     104             : 
     105           0 :         if (geteuid() != 0) {
     106           0 :                 set_effective_uid(0);
     107             : 
     108           0 :                 if (geteuid() != 0) {
     109           0 :                         DEBUG(0,
     110             :                               ("Warning: You appear to have a trapdoor "
     111             :                                "uid system\n"));
     112             :                 }
     113             :         }
     114             : 
     115           0 :         if (getegid() != 0) {
     116           0 :                 set_effective_gid(0);
     117             : 
     118           0 :                 if (getegid() != 0) {
     119           0 :                         DEBUG(0,
     120             :                               ("Warning: You appear to have a trapdoor "
     121             :                                "gid system\n"));
     122             :                 }
     123             :         }
     124             : }
     125             : 
     126             : /****************************************************************************
     127             :  Get the list of current groups.
     128             : ****************************************************************************/
     129             : 
     130          44 : static int get_current_groups(gid_t gid, uint32_t *p_ngroups, gid_t **p_groups)
     131             : {
     132             :         int i;
     133             :         int ngroups;
     134          44 :         gid_t *groups = NULL;
     135             : 
     136          44 :         (*p_ngroups) = 0;
     137          44 :         (*p_groups) = NULL;
     138             : 
     139             :         /* this looks a little strange, but is needed to cope with
     140             :            systems that put the current egid in the group list
     141             :            returned from getgroups() (tridge) */
     142          44 :         save_re_gid();
     143          44 :         set_effective_gid(gid);
     144          44 :         samba_setgid(gid);
     145             : 
     146          44 :         ngroups = sys_getgroups(0, NULL);
     147          44 :         if (ngroups <= 0) {
     148           0 :                 goto fail;
     149             :         }
     150             : 
     151          44 :         if((groups = SMB_MALLOC_ARRAY(gid_t, ngroups+1)) == NULL) {
     152           0 :                 DEBUG(0,("setup_groups malloc fail !\n"));
     153           0 :                 goto fail;
     154             :         }
     155             : 
     156          44 :         if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
     157           0 :                 goto fail;
     158             :         }
     159             : 
     160          44 :         restore_re_gid();
     161             : 
     162          44 :         (*p_ngroups) = ngroups;
     163          44 :         (*p_groups) = groups;
     164             : 
     165          44 :         DEBUG( 4, ( "get_current_groups: user is in %u groups: ", ngroups));
     166          88 :         for (i = 0; i < ngroups; i++ ) {
     167          44 :                 DEBUG( 4, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
     168             :         }
     169          44 :         DEBUG( 4, ( "\n" ) );
     170             : 
     171          44 :         return ngroups;
     172             : 
     173           0 : fail:
     174           0 :         SAFE_FREE(groups);
     175           0 :         restore_re_gid();
     176           0 :         return -1;
     177             : }
     178             : 
     179             : /****************************************************************************
     180             :  Create a new security context on the stack.  It is the same as the old
     181             :  one.  User changes are done using the set_sec_ctx() function.
     182             : ****************************************************************************/
     183             : 
     184       68868 : bool push_sec_ctx(void)
     185             : {
     186             :         struct sec_ctx *ctx_p;
     187             : 
     188       68868 :         START_PROFILE(push_sec_ctx);
     189             : 
     190             :         /* Check we don't overflow our stack */
     191             : 
     192       68868 :         if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
     193           0 :                 DEBUG(0, ("Security context stack overflow!\n"));
     194           0 :                 smb_panic("Security context stack overflow!");
     195             :         }
     196             : 
     197             :         /* Store previous user context */
     198             : 
     199       68868 :         sec_ctx_stack_ndx++;
     200             : 
     201       68868 :         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
     202             : 
     203       68868 :         ctx_p->ut.uid = geteuid();
     204       68868 :         ctx_p->ut.gid = getegid();
     205             : 
     206       68868 :         DEBUG(4, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", 
     207             :                   (unsigned int)ctx_p->ut.uid, (unsigned int)ctx_p->ut.gid, sec_ctx_stack_ndx ));
     208             : 
     209       92003 :         ctx_p->token = dup_nt_token(NULL,
     210       68868 :                                     sec_ctx_stack[sec_ctx_stack_ndx-1].token);
     211             : 
     212       68868 :         ctx_p->ut.ngroups = sys_getgroups(0, NULL);
     213             : 
     214       68868 :         if (ctx_p->ut.ngroups != 0) {
     215       17211 :                 if (!(ctx_p->ut.groups = SMB_MALLOC_ARRAY(gid_t, ctx_p->ut.ngroups))) {
     216           0 :                         DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
     217           0 :                         TALLOC_FREE(ctx_p->token);
     218           0 :                         return False;
     219             :                 }
     220             : 
     221       17211 :                 sys_getgroups(ctx_p->ut.ngroups, ctx_p->ut.groups);
     222             :         } else {
     223       51657 :                 ctx_p->ut.groups = NULL;
     224             :         }
     225             : 
     226       68868 :         END_PROFILE(push_sec_ctx);
     227             : 
     228       68868 :         return True;
     229             : }
     230             : 
     231             : #ifndef HAVE_DARWIN_INITGROUPS
     232             : /****************************************************************************
     233             :  Become the specified uid and gid.
     234             : ****************************************************************************/
     235             : 
     236      221519 : static bool become_id(uid_t uid, gid_t gid)
     237             : {
     238      221519 :         return become_gid(gid) && become_uid(uid);
     239             : }
     240             : 
     241             : /****************************************************************************
     242             :  Change UNIX security context. Calls panic if not successful so no return value.
     243             : ****************************************************************************/
     244             : /* Normal credential switch path. */
     245             : 
     246      221519 : static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
     247             : {
     248             :         /* Start context switch */
     249      221519 :         gain_root();
     250             : #ifdef HAVE_SETGROUPS
     251      221519 :         if (sys_setgroups(gid, ngroups, groups) != 0 && !non_root_mode()) {
     252           0 :                 smb_panic("sys_setgroups failed");
     253             :         }
     254             : #endif
     255      221519 :         become_id(uid, gid);
     256             :         /* end context switch */
     257      221519 : }
     258             : 
     259             : #else /* HAVE_DARWIN_INITGROUPS */
     260             : 
     261             : /* The Darwin groups implementation is a little unusual. The list of
     262             : * groups in the kernel credential is not exhaustive, but more like
     263             : * a cache. The full group list is held in userspace and checked
     264             : * dynamically.
     265             : *
     266             : * This is an optional mechanism, and setgroups(2) opts out
     267             : * of it. That is, if you call setgroups, then the list of groups you
     268             : * set are the only groups that are ever checked. This is not what we
     269             : * want. We want to opt in to the dynamic resolution mechanism, so we
     270             : * need to specify the uid of the user whose group list (cache) we are
     271             : * setting.
     272             : *
     273             : * The Darwin rules are:
     274             : *  1. Thou shalt setegid, initgroups and seteuid IN THAT ORDER
     275             : *  2. Thou shalt not pass more that NGROUPS_MAX to initgroups
     276             : *  3. Thou shalt leave the first entry in the groups list well alone
     277             : */
     278             : 
     279             : #include <sys/syscall.h>
     280             : 
     281             : static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
     282             : {
     283             :         int max = NGROUPS_MAX;
     284             : 
     285             :         /* Start context switch */
     286             :         gain_root();
     287             : 
     288             :         become_gid(gid);
     289             : 
     290             : 
     291             :         if (syscall(SYS_initgroups, (ngroups > max) ? max : ngroups,
     292             :                         groups, uid) == -1 && !non_root_mode()) {
     293             :                 DEBUG(0, ("WARNING: failed to set group list "
     294             :                         "(%d groups) for UID %d: %s\n",
     295             :                         ngroups, uid, strerror(errno)));
     296             :                 smb_panic("sys_setgroups failed");
     297             :         }
     298             : 
     299             :         become_uid(uid);
     300             :         /* end context switch */
     301             : }
     302             : 
     303             : #endif /* HAVE_DARWIN_INITGROUPS */
     304             : 
     305             : /****************************************************************************
     306             :  Set the current security context to a given user.
     307             : ****************************************************************************/
     308             : 
     309      152656 : static void set_sec_ctx_internal(uid_t uid, gid_t gid,
     310             :                                  int ngroups, gid_t *groups,
     311             :                                  const struct security_token *token)
     312             : {
     313      152656 :         struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
     314             : 
     315             :         /* Set the security context */
     316             : 
     317      152656 :         DEBUG(4, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
     318             :                 (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
     319             : 
     320      152656 :         security_token_debug(DBGC_CLASS, 5, token);
     321      152656 :         debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
     322             : 
     323             :         /* Change uid, gid and supplementary group list. */
     324      152656 :         set_unix_security_ctx(uid, gid, ngroups, groups);
     325             : 
     326      152656 :         ctx_p->ut.ngroups = ngroups;
     327             : 
     328      152656 :         SAFE_FREE(ctx_p->ut.groups);
     329      152656 :         if (token && (token == ctx_p->token)) {
     330           0 :                 smb_panic("DUPLICATE_TOKEN");
     331             :         }
     332             : 
     333      152656 :         TALLOC_FREE(ctx_p->token);
     334             : 
     335      152656 :         if (ngroups) {
     336       21871 :                 ctx_p->ut.groups = (gid_t *)smb_xmemdup(groups,
     337             :                                                         sizeof(gid_t) * ngroups);
     338             :         } else {
     339      130785 :                 ctx_p->ut.groups = NULL;
     340             :         }
     341             : 
     342      152656 :         if (token) {
     343       21871 :                 ctx_p->token = dup_nt_token(NULL, token);
     344       21871 :                 if (!ctx_p->token) {
     345           0 :                         smb_panic("dup_nt_token failed");
     346             :                 }
     347             :         } else {
     348      130785 :                 ctx_p->token = NULL;
     349             :         }
     350             : 
     351      152656 :         ctx_p->ut.uid = uid;
     352      152656 :         ctx_p->ut.gid = gid;
     353             : 
     354             :         /* Update current_user stuff */
     355             : 
     356      152656 :         current_user.ut.uid = uid;
     357      152656 :         current_user.ut.gid = gid;
     358      152656 :         current_user.ut.ngroups = ngroups;
     359      152656 :         current_user.ut.groups = groups;
     360      152656 :         current_user.nt_user_token = ctx_p->token;
     361             : 
     362             :         /*
     363             :          * Delete any ChDir cache. We can't assume
     364             :          * the new uid has access to current working
     365             :          * directory.
     366             :          * BUG: https://bugzilla.samba.org/show_bug.cgi?id=14682
     367             :          */
     368      152656 :         SAFE_FREE(LastDir);
     369      152656 : }
     370             : 
     371       21871 : void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, const struct security_token *token)
     372             : {
     373       21871 :         START_PROFILE(set_sec_ctx);
     374       21871 :         set_sec_ctx_internal(uid, gid, ngroups, groups, token);
     375       21871 :         END_PROFILE(set_sec_ctx);
     376       21871 : }
     377             : 
     378             : /****************************************************************************
     379             :  Become root context.
     380             : ****************************************************************************/
     381             : 
     382      130785 : void set_root_sec_ctx(void)
     383             : {
     384             :         /* May need to worry about supplementary groups at some stage */
     385             : 
     386      130785 :         START_PROFILE(set_root_sec_ctx);
     387      130785 :         set_sec_ctx_internal(0, 0, 0, NULL, NULL);
     388      130785 :         END_PROFILE(set_root_sec_ctx);
     389      130785 : }
     390             : 
     391             : /****************************************************************************
     392             :  Pop a security context from the stack.
     393             : ****************************************************************************/
     394             : 
     395       68863 : bool pop_sec_ctx(void)
     396             : {
     397             :         struct sec_ctx *ctx_p;
     398             :         struct sec_ctx *prev_ctx_p;
     399             : 
     400       68863 :         START_PROFILE(pop_sec_ctx);
     401             : 
     402             :         /* Check for stack underflow */
     403             : 
     404       68863 :         if (sec_ctx_stack_ndx == 0) {
     405           0 :                 DEBUG(0, ("Security context stack underflow!\n"));
     406           0 :                 smb_panic("Security context stack underflow!");
     407             :         }
     408             : 
     409       68863 :         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
     410             : 
     411             :         /* Clear previous user info */
     412             : 
     413       68863 :         ctx_p->ut.uid = (uid_t)-1;
     414       68863 :         ctx_p->ut.gid = (gid_t)-1;
     415             : 
     416       68863 :         SAFE_FREE(ctx_p->ut.groups);
     417       68863 :         ctx_p->ut.ngroups = 0;
     418             : 
     419       68863 :         TALLOC_FREE(ctx_p->token);
     420             : 
     421             :         /* Pop back previous user */
     422             : 
     423       68863 :         sec_ctx_stack_ndx--;
     424             : 
     425       68863 :         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
     426             : 
     427             :         /* Change uid, gid and supplementary group list. */
     428      114591 :         set_unix_security_ctx(prev_ctx_p->ut.uid,
     429             :                         prev_ctx_p->ut.gid,
     430       68863 :                         prev_ctx_p->ut.ngroups,
     431             :                         prev_ctx_p->ut.groups);
     432             : 
     433             :         /* Update current_user stuff */
     434             : 
     435       68863 :         current_user.ut.uid = prev_ctx_p->ut.uid;
     436       68863 :         current_user.ut.gid = prev_ctx_p->ut.gid;
     437       68863 :         current_user.ut.ngroups = prev_ctx_p->ut.ngroups;
     438       68863 :         current_user.ut.groups = prev_ctx_p->ut.groups;
     439       68863 :         current_user.nt_user_token = prev_ctx_p->token;
     440             : 
     441       68863 :         END_PROFILE(pop_sec_ctx);
     442             : 
     443       68863 :         DEBUG(4, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
     444             :                 (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
     445             : 
     446       68863 :         return True;
     447             : }
     448             : 
     449             : /* Initialise the security context system */
     450             : 
     451          44 : void init_sec_ctx(void)
     452             : {
     453             :         int i;
     454             :         struct sec_ctx *ctx_p;
     455             : 
     456             :         /* Initialise security context stack */
     457             : 
     458          44 :         memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
     459             : 
     460         396 :         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
     461         352 :                 sec_ctx_stack[i].ut.uid = (uid_t)-1;
     462         352 :                 sec_ctx_stack[i].ut.gid = (gid_t)-1;
     463             :         }
     464             : 
     465             :         /* Initialise first level of stack.  It is the current context */
     466          44 :         ctx_p = &sec_ctx_stack[0];
     467             : 
     468          44 :         ctx_p->ut.uid = geteuid();
     469          44 :         ctx_p->ut.gid = getegid();
     470             : 
     471          44 :         get_current_groups(ctx_p->ut.gid, &ctx_p->ut.ngroups, &ctx_p->ut.groups);
     472             : 
     473          44 :         ctx_p->token = NULL; /* Maps to guest user. */
     474             : 
     475             :         /* Initialise current_user global */
     476             : 
     477          44 :         current_user.ut.uid = ctx_p->ut.uid;
     478          44 :         current_user.ut.gid = ctx_p->ut.gid;
     479          44 :         current_user.ut.ngroups = ctx_p->ut.ngroups;
     480          44 :         current_user.ut.groups = ctx_p->ut.groups;
     481             : 
     482             :         /* The conn and vuid are usually taken care of by other modules.
     483             :            We initialise them here. */
     484             : 
     485          44 :         current_user.conn = NULL;
     486          44 :         current_user.vuid = UID_FIELD_INVALID;
     487          44 :         current_user.nt_user_token = NULL;
     488          44 : }
     489             : 
     490             : /*************************************************************
     491             :  Called when we're inside a become_root() temporary escalation
     492             :  of privileges and the nt_user_token is NULL. Return the last
     493             :  active token on the context stack. We know there is at least
     494             :  one valid non-NULL token on the stack so panic if we underflow.
     495             : *************************************************************/
     496             : 
     497           0 : const struct security_token *sec_ctx_active_token(void)
     498             : {
     499           0 :         int stack_index = sec_ctx_stack_ndx;
     500           0 :         struct sec_ctx *ctx_p = &sec_ctx_stack[stack_index];
     501             : 
     502           0 :         while (ctx_p->token == NULL) {
     503           0 :                 stack_index--;
     504           0 :                 if (stack_index < 0) {
     505           0 :                         DEBUG(0, ("Security context active token "
     506             :                                   "stack underflow!\n"));
     507           0 :                         smb_panic("Security context active token "
     508             :                                   "stack underflow!");
     509             :                 }
     510           0 :                 ctx_p = &sec_ctx_stack[stack_index];
     511             :         }
     512           0 :         return ctx_p->token;
     513             : }

Generated by: LCOV version 1.13