root/source3/smbd/sec_ctx.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. unix_token_equal
  2. become_uid
  3. become_gid
  4. become_id
  5. gain_root
  6. get_current_groups
  7. push_sec_ctx
  8. set_unix_security_ctx
  9. set_unix_security_ctx
  10. set_sec_ctx
  11. set_root_sec_ctx
  12. pop_sec_ctx
  13. init_sec_ctx

   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 "smbd/globals.h"
  22 
  23 extern struct current_user current_user;
  24 
  25 /****************************************************************************
  26  Are two UNIX tokens equal ?
  27 ****************************************************************************/
  28 
  29 bool unix_token_equal(const UNIX_USER_TOKEN *t1, const UNIX_USER_TOKEN *t2)
     /* [<][>][^][v][top][bottom][index][help] */
  30 {
  31         if (t1->uid != t2->uid || t1->gid != t2->gid ||
  32                         t1->ngroups != t2->ngroups) {
  33                 return false;
  34         }
  35         if (memcmp(t1->groups, t2->groups,
  36                         t1->ngroups*sizeof(gid_t)) != 0) {
  37                 return false;
  38         }
  39         return true;
  40 }
  41 
  42 /****************************************************************************
  43  Become the specified uid.
  44 ****************************************************************************/
  45 
  46 static bool become_uid(uid_t uid)
     /* [<][>][^][v][top][bottom][index][help] */
  47 {
  48         /* Check for dodgy uid values */
  49 
  50         if (uid == (uid_t)-1 || 
  51             ((sizeof(uid_t) == 2) && (uid == (uid_t)65535))) {
  52                 if (!become_uid_done) {
  53                         DEBUG(1,("WARNING: using uid %d is a security risk\n",
  54                                  (int)uid));
  55                         become_uid_done = true;
  56                 }
  57         }
  58 
  59         /* Set effective user id */
  60 
  61         set_effective_uid(uid);
  62 
  63         DO_PROFILE_INC(uid_changes);
  64         return True;
  65 }
  66 
  67 /****************************************************************************
  68  Become the specified gid.
  69 ****************************************************************************/
  70 
  71 static bool become_gid(gid_t gid)
     /* [<][>][^][v][top][bottom][index][help] */
  72 {
  73         /* Check for dodgy gid values */
  74 
  75         if (gid == (gid_t)-1 || ((sizeof(gid_t) == 2) && 
  76                                  (gid == (gid_t)65535))) {
  77                 if (!become_gid_done) {
  78                         DEBUG(1,("WARNING: using gid %d is a security risk\n",
  79                                  (int)gid));  
  80                         become_gid_done = true;
  81                 }
  82         }
  83   
  84         /* Set effective group id */
  85 
  86         set_effective_gid(gid);
  87         return True;
  88 }
  89 
  90 /****************************************************************************
  91  Become the specified uid and gid.
  92 ****************************************************************************/
  93 
  94 static bool become_id(uid_t uid, gid_t gid)
     /* [<][>][^][v][top][bottom][index][help] */
  95 {
  96         return become_gid(gid) && become_uid(uid);
  97 }
  98 
  99 /****************************************************************************
 100  Drop back to root privileges in order to change to another user.
 101 ****************************************************************************/
 102 
 103 static void gain_root(void)
     /* [<][>][^][v][top][bottom][index][help] */
 104 {
 105         if (non_root_mode()) {
 106                 return;
 107         }
 108 
 109         if (geteuid() != 0) {
 110                 set_effective_uid(0);
 111 
 112                 if (geteuid() != 0) {
 113                         DEBUG(0,
 114                               ("Warning: You appear to have a trapdoor "
 115                                "uid system\n"));
 116                 }
 117         }
 118 
 119         if (getegid() != 0) {
 120                 set_effective_gid(0);
 121 
 122                 if (getegid() != 0) {
 123                         DEBUG(0,
 124                               ("Warning: You appear to have a trapdoor "
 125                                "gid system\n"));
 126                 }
 127         }
 128 }
 129 
 130 /****************************************************************************
 131  Get the list of current groups.
 132 ****************************************************************************/
 133 
 134 static int get_current_groups(gid_t gid, size_t *p_ngroups, gid_t **p_groups)
     /* [<][>][^][v][top][bottom][index][help] */
 135 {
 136         int i;
 137         gid_t grp;
 138         int ngroups;
 139         gid_t *groups = NULL;
 140 
 141         (*p_ngroups) = 0;
 142         (*p_groups) = NULL;
 143 
 144         /* this looks a little strange, but is needed to cope with
 145            systems that put the current egid in the group list
 146            returned from getgroups() (tridge) */
 147         save_re_gid();
 148         set_effective_gid(gid);
 149         setgid(gid);
 150 
 151         ngroups = sys_getgroups(0,&grp);
 152         if (ngroups <= 0) {
 153                 goto fail;
 154         }
 155 
 156         if((groups = SMB_MALLOC_ARRAY(gid_t, ngroups+1)) == NULL) {
 157                 DEBUG(0,("setup_groups malloc fail !\n"));
 158                 goto fail;
 159         }
 160 
 161         if ((ngroups = sys_getgroups(ngroups,groups)) == -1) {
 162                 goto fail;
 163         }
 164 
 165         restore_re_gid();
 166 
 167         (*p_ngroups) = ngroups;
 168         (*p_groups) = groups;
 169 
 170         DEBUG( 3, ( "get_current_groups: user is in %u groups: ", ngroups));
 171         for (i = 0; i < ngroups; i++ ) {
 172                 DEBUG( 3, ( "%s%d", (i ? ", " : ""), (int)groups[i] ) );
 173         }
 174         DEBUG( 3, ( "\n" ) );
 175 
 176         return ngroups;
 177 
 178 fail:
 179         SAFE_FREE(groups);
 180         restore_re_gid();
 181         return -1;
 182 }
 183 
 184 /****************************************************************************
 185  Create a new security context on the stack.  It is the same as the old
 186  one.  User changes are done using the set_sec_ctx() function.
 187 ****************************************************************************/
 188 
 189 bool push_sec_ctx(void)
     /* [<][>][^][v][top][bottom][index][help] */
 190 {
 191         struct sec_ctx *ctx_p;
 192 
 193         /* Check we don't overflow our stack */
 194 
 195         if (sec_ctx_stack_ndx == MAX_SEC_CTX_DEPTH) {
 196                 DEBUG(0, ("Security context stack overflow!\n"));
 197                 smb_panic("Security context stack overflow!");
 198         }
 199 
 200         /* Store previous user context */
 201 
 202         sec_ctx_stack_ndx++;
 203 
 204         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
 205 
 206         ctx_p->ut.uid = geteuid();
 207         ctx_p->ut.gid = getegid();
 208 
 209         DEBUG(3, ("push_sec_ctx(%u, %u) : sec_ctx_stack_ndx = %d\n", 
 210                   (unsigned int)ctx_p->ut.uid, (unsigned int)ctx_p->ut.gid, sec_ctx_stack_ndx ));
 211 
 212         ctx_p->token = dup_nt_token(NULL,
 213                                     sec_ctx_stack[sec_ctx_stack_ndx-1].token);
 214 
 215         ctx_p->ut.ngroups = sys_getgroups(0, NULL);
 216 
 217         if (ctx_p->ut.ngroups != 0) {
 218                 if (!(ctx_p->ut.groups = SMB_MALLOC_ARRAY(gid_t, ctx_p->ut.ngroups))) {
 219                         DEBUG(0, ("Out of memory in push_sec_ctx()\n"));
 220                         TALLOC_FREE(ctx_p->token);
 221                         return False;
 222                 }
 223 
 224                 sys_getgroups(ctx_p->ut.ngroups, ctx_p->ut.groups);
 225         } else {
 226                 ctx_p->ut.groups = NULL;
 227         }
 228 
 229         return True;
 230 }
 231 
 232 /****************************************************************************
 233  Change UNIX security context. Calls panic if not successful so no return value.
 234 ****************************************************************************/
 235 
 236 #ifndef HAVE_DARWIN_INITGROUPS
 237 
 238 /* Normal credential switch path. */
 239 
 240 static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
     /* [<][>][^][v][top][bottom][index][help] */
 241 {
 242         /* Start context switch */
 243         gain_root();
 244 #ifdef HAVE_SETGROUPS
 245         if (sys_setgroups(gid, ngroups, groups) != 0 && !non_root_mode()) {
 246                 smb_panic("sys_setgroups failed");
 247         }
 248 #endif
 249         become_id(uid, gid);
 250         /* end context switch */
 251 }
 252 
 253 #else /* HAVE_DARWIN_INITGROUPS */
 254 
 255 /* The Darwin groups implementation is a little unusual. The list of
 256 * groups in the kernel credential is not exhaustive, but more like
 257 * a cache. The full group list is held in userspace and checked
 258 * dynamically.
 259 *
 260 * This is an optional mechanism, and setgroups(2) opts out
 261 * of it. That is, if you call setgroups, then the list of groups you
 262 * set are the only groups that are ever checked. This is not what we
 263 * want. We want to opt in to the dynamic resolution mechanism, so we
 264 * need to specify the uid of the user whose group list (cache) we are
 265 * setting.
 266 *
 267 * The Darwin rules are:
 268 *  1. Thou shalt setegid, initgroups and seteuid IN THAT ORDER
 269 *  2. Thou shalt not pass more that NGROUPS_MAX to initgroups
 270 *  3. Thou shalt leave the first entry in the groups list well alone
 271 */
 272 
 273 #include <sys/syscall.h>
 274 
 275 static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups)
     /* [<][>][^][v][top][bottom][index][help] */
 276 {
 277         int max = groups_max();
 278 
 279         /* Start context switch */
 280         gain_root();
 281 
 282         become_gid(gid);
 283 
 284 
 285         if (syscall(SYS_initgroups, (ngroups > max) ? max : ngroups,
 286                         groups, uid) == -1 && !non_root_mode()) {
 287                 DEBUG(0, ("WARNING: failed to set group list "
 288                         "(%d groups) for UID %ld: %s\n",
 289                         ngroups, uid, strerror(errno)));
 290                 smb_panic("sys_setgroups failed");
 291         }
 292 
 293         become_uid(uid);
 294         /* end context switch */
 295 }
 296 
 297 #endif /* HAVE_DARWIN_INITGROUPS */
 298 
 299 /****************************************************************************
 300  Set the current security context to a given user.
 301 ****************************************************************************/
 302 
 303 void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token)
     /* [<][>][^][v][top][bottom][index][help] */
 304 {
 305         struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
 306         
 307         /* Set the security context */
 308 
 309         DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
 310                 (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx));
 311 
 312         debug_nt_user_token(DBGC_CLASS, 5, token);
 313         debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups);
 314 
 315         /* Change uid, gid and supplementary group list. */
 316         set_unix_security_ctx(uid, gid, ngroups, groups);
 317 
 318         ctx_p->ut.ngroups = ngroups;
 319 
 320         SAFE_FREE(ctx_p->ut.groups);
 321         if (token && (token == ctx_p->token)) {
 322                 smb_panic("DUPLICATE_TOKEN");
 323         }
 324 
 325         TALLOC_FREE(ctx_p->token);
 326         
 327         if (ngroups) {
 328                 ctx_p->ut.groups = (gid_t *)memdup(groups,
 329                                                    sizeof(gid_t) * ngroups);
 330                 if (!ctx_p->ut.groups) {
 331                         smb_panic("memdup failed");
 332                 }
 333         } else {
 334                 ctx_p->ut.groups = NULL;
 335         }
 336 
 337         if (token) {
 338                 ctx_p->token = dup_nt_token(NULL, token);
 339                 if (!ctx_p->token) {
 340                         smb_panic("dup_nt_token failed");
 341                 }
 342         } else {
 343                 ctx_p->token = NULL;
 344         }
 345 
 346         ctx_p->ut.uid = uid;
 347         ctx_p->ut.gid = gid;
 348 
 349         /* Update current_user stuff */
 350 
 351         current_user.ut.uid = uid;
 352         current_user.ut.gid = gid;
 353         current_user.ut.ngroups = ngroups;
 354         current_user.ut.groups = groups;
 355         current_user.nt_user_token = ctx_p->token;
 356 }
 357 
 358 /****************************************************************************
 359  Become root context.
 360 ****************************************************************************/
 361 
 362 void set_root_sec_ctx(void)
     /* [<][>][^][v][top][bottom][index][help] */
 363 {
 364         /* May need to worry about supplementary groups at some stage */
 365 
 366         set_sec_ctx(0, 0, 0, NULL, NULL);
 367 }
 368 
 369 /****************************************************************************
 370  Pop a security context from the stack.
 371 ****************************************************************************/
 372 
 373 bool pop_sec_ctx(void)
     /* [<][>][^][v][top][bottom][index][help] */
 374 {
 375         struct sec_ctx *ctx_p;
 376         struct sec_ctx *prev_ctx_p;
 377 
 378         /* Check for stack underflow */
 379 
 380         if (sec_ctx_stack_ndx == 0) {
 381                 DEBUG(0, ("Security context stack underflow!\n"));
 382                 smb_panic("Security context stack underflow!");
 383         }
 384 
 385         ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
 386 
 387         /* Clear previous user info */
 388 
 389         ctx_p->ut.uid = (uid_t)-1;
 390         ctx_p->ut.gid = (gid_t)-1;
 391 
 392         SAFE_FREE(ctx_p->ut.groups);
 393         ctx_p->ut.ngroups = 0;
 394 
 395         TALLOC_FREE(ctx_p->token);
 396 
 397         /* Pop back previous user */
 398 
 399         sec_ctx_stack_ndx--;
 400 
 401         prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx];
 402 
 403         /* Change uid, gid and supplementary group list. */
 404         set_unix_security_ctx(prev_ctx_p->ut.uid,
 405                         prev_ctx_p->ut.gid,
 406                         prev_ctx_p->ut.ngroups,
 407                         prev_ctx_p->ut.groups);
 408 
 409         /* Update current_user stuff */
 410 
 411         current_user.ut.uid = prev_ctx_p->ut.uid;
 412         current_user.ut.gid = prev_ctx_p->ut.gid;
 413         current_user.ut.ngroups = prev_ctx_p->ut.ngroups;
 414         current_user.ut.groups = prev_ctx_p->ut.groups;
 415         current_user.nt_user_token = prev_ctx_p->token;
 416 
 417         DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", 
 418                 (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx));
 419 
 420         return True;
 421 }
 422 
 423 /* Initialise the security context system */
 424 
 425 void init_sec_ctx(void)
     /* [<][>][^][v][top][bottom][index][help] */
 426 {
 427         int i;
 428         struct sec_ctx *ctx_p;
 429 
 430         /* Initialise security context stack */
 431 
 432         memset(sec_ctx_stack, 0, sizeof(struct sec_ctx) * MAX_SEC_CTX_DEPTH);
 433 
 434         for (i = 0; i < MAX_SEC_CTX_DEPTH; i++) {
 435                 sec_ctx_stack[i].ut.uid = (uid_t)-1;
 436                 sec_ctx_stack[i].ut.gid = (gid_t)-1;
 437         }
 438 
 439         /* Initialise first level of stack.  It is the current context */
 440         ctx_p = &sec_ctx_stack[0];
 441 
 442         ctx_p->ut.uid = geteuid();
 443         ctx_p->ut.gid = getegid();
 444 
 445         get_current_groups(ctx_p->ut.gid, &ctx_p->ut.ngroups, &ctx_p->ut.groups);
 446 
 447         ctx_p->token = NULL; /* Maps to guest user. */
 448 
 449         /* Initialise current_user global */
 450 
 451         current_user.ut.uid = ctx_p->ut.uid;
 452         current_user.ut.gid = ctx_p->ut.gid;
 453         current_user.ut.ngroups = ctx_p->ut.ngroups;
 454         current_user.ut.groups = ctx_p->ut.groups;
 455 
 456         /* The conn and vuid are usually taken care of by other modules.
 457            We initialise them here. */
 458 
 459         current_user.conn = NULL;
 460         current_user.vuid = UID_FIELD_INVALID;
 461         current_user.nt_user_token = NULL;
 462 }

/* [<][>][^][v][top][bottom][index][help] */