void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token) { struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx]; /* Set the security context */ DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx)); debug_nt_user_token(DBGC_CLASS, 5, token); debug_unix_user_token(DBGC_CLASS, 5, uid, gid, ngroups, groups); gain_root(); #ifdef HAVE_SETGROUPS sys_setgroups(ngroups, groups); #endif ctx_p->ut.ngroups = ngroups; SAFE_FREE(ctx_p->ut.groups); if (token && (token == ctx_p->token)) { smb_panic("DUPLICATE_TOKEN"); } TALLOC_FREE(ctx_p->token); if (ngroups) { ctx_p->ut.groups = (gid_t *)memdup(groups, sizeof(gid_t) * ngroups); if (!ctx_p->ut.groups) { smb_panic("memdup failed"); } } else { ctx_p->ut.groups = NULL; } if (token) { ctx_p->token = dup_nt_token(NULL, token); if (!ctx_p->token) { smb_panic("dup_nt_token failed"); } } else { ctx_p->token = NULL; } become_id(uid, gid); ctx_p->ut.uid = uid; ctx_p->ut.gid = gid; /* Update current_user stuff */ current_user.ut.uid = uid; current_user.ut.gid = gid; current_user.ut.ngroups = ngroups; current_user.ut.groups = groups; current_user.nt_user_token = ctx_p->token; }
static void set_unix_security_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups) { /* Start context switch */ gain_root(); #ifdef HAVE_SETGROUPS if (sys_setgroups(gid, ngroups, groups) != 0 && !non_root_mode()) { smb_panic("sys_setgroups failed"); } #endif become_id(uid, gid); /* end context switch */ }
BOOL pop_sec_ctx(void) { struct sec_ctx *ctx_p; struct sec_ctx *prev_ctx_p; /* Check for stack underflow */ if (sec_ctx_stack_ndx == 0) { DEBUG(0, ("Security context stack underflow!\n")); smb_panic("Security context stack underflow!\n"); } ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx]; /* Clear previous user info */ ctx_p->uid = (uid_t)-1; ctx_p->gid = (gid_t)-1; SAFE_FREE(ctx_p->groups); ctx_p->ngroups = 0; delete_nt_token(&ctx_p->token); /* Pop back previous user */ sec_ctx_stack_ndx--; gain_root(); prev_ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx]; #ifdef HAVE_SETGROUPS sys_setgroups(prev_ctx_p->ngroups, prev_ctx_p->groups); #endif become_id(prev_ctx_p->uid, prev_ctx_p->gid); /* Update current_user stuff */ current_user.uid = prev_ctx_p->uid; current_user.gid = prev_ctx_p->gid; current_user.ngroups = prev_ctx_p->ngroups; current_user.groups = prev_ctx_p->groups; current_user.nt_user_token = prev_ctx_p->token; DEBUG(3, ("pop_sec_ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", (unsigned int)geteuid(), (unsigned int)getegid(), sec_ctx_stack_ndx)); return True; }
void set_sec_ctx(uid_t uid, gid_t gid, int ngroups, gid_t *groups, NT_USER_TOKEN *token) { struct sec_ctx *ctx_p = &sec_ctx_stack[sec_ctx_stack_ndx]; /* Set the security context */ DEBUG(3, ("setting sec ctx (%u, %u) - sec_ctx_stack_ndx = %d\n", (unsigned int)uid, (unsigned int)gid, sec_ctx_stack_ndx)); if (ngroups) { int i; DEBUG(3, ("%d user groups: \n", ngroups)); for (i = 0; i < ngroups; i++) { DEBUGADD(3, ("%u ", (unsigned int)groups[i])); } DEBUG(3, ("\n")); } gain_root(); #ifdef HAVE_SETGROUPS sys_setgroups(ngroups, groups); #endif ctx_p->ngroups = ngroups; SAFE_FREE(ctx_p->groups); if (token && (token == ctx_p->token)) smb_panic("DUPLICATE_TOKEN"); delete_nt_token(&ctx_p->token); ctx_p->groups = memdup(groups, sizeof(gid_t) * ngroups); ctx_p->token = dup_nt_token(token); become_id(uid, gid); ctx_p->uid = uid; ctx_p->gid = gid; /* Update current_user stuff */ current_user.uid = uid; current_user.gid = gid; current_user.ngroups = ngroups; current_user.groups = groups; current_user.nt_user_token = ctx_p->token; }
/**************************************************************************** some systems don't have an initgroups call ****************************************************************************/ int initgroups(char *name,gid_t id) { #ifndef HAVE_SETGROUPS static int done; if (!done) { DEBUG(1,("WARNING: running without setgroups\n")); done=1; } /* yikes! no SETGROUPS or INITGROUPS? how can this work? */ return(0); #else /* HAVE_SETGROUPS */ gid_t *grouplst = NULL; int max_gr = groups_max(); int ret; int i,j; struct group *g; char *gr; if((grouplst = SMB_MALLOC_ARRAY(gid_t, max_gr)) == NULL) { DEBUG(0,("initgroups: malloc fail !\n")); return -1; } grouplst[0] = id; i = 1; while (i < max_gr && ((g = (struct group *)getgrent()) != (struct group *)NULL)) { if (g->gr_gid == id) continue; j = 0; gr = g->gr_mem[0]; while (gr && (*gr != (char)NULL)) { if (strcmp(name,gr) == 0) { grouplst[i] = g->gr_gid; i++; gr = (char *)NULL; break; } gr = g->gr_mem[++j]; } } endgrent(); ret = sys_setgroups(i,grouplst); SAFE_FREE(grouplst); return ret; #endif /* HAVE_SETGROUPS */ }
/* This is a *much* faster way of getting the list of groups for a user without changing the current supplemenrary group list. The old method used getgrent() which could take 20 minutes on a really big network with hundeds of thousands of groups and users. The new method takes a couple of seconds. NOTE!! this function only works if it is called as root! */ static int getgrouplist_internals(const char *user, gid_t gid, gid_t *groups, int *grpcnt) { gid_t *gids_saved; int ret, ngrp_saved, num_gids; if (non_root_mode()) { *grpcnt = 0; return 0; } /* work out how many groups we need to save */ ngrp_saved = getgroups(0, NULL); if (ngrp_saved == -1) { /* this shouldn't happen */ return -1; } gids_saved = (gid_t *)malloc(sizeof(gid_t) * (ngrp_saved+1)); if (!gids_saved) { errno = ENOMEM; return -1; } ngrp_saved = getgroups(ngrp_saved, gids_saved); if (ngrp_saved == -1) { SAFE_FREE(gids_saved); /* very strange! */ return -1; } if (initgroups(user, gid) != 0) { DEBUG(0, ("getgrouplist_internals: initgroups() failed!\n")); SAFE_FREE(gids_saved); return -1; } /* this must be done to cope with systems that put the current egid in the return from getgroups() */ save_re_gid(); set_effective_gid(gid); setgid(gid); num_gids = getgroups(0, NULL); if (num_gids + 1 > *grpcnt) { *grpcnt = num_gids + 1; ret = -1; } else { ret = getgroups(*grpcnt - 1, &groups[1]); if (ret >= 0) { groups[0] = gid; *grpcnt = ret + 1; } } restore_re_gid(); if (sys_setgroups(ngrp_saved, gids_saved) != 0) { /* yikes! */ DEBUG(0,("ERROR: getgrouplist: failed to reset group list!\n")); smb_panic("getgrouplist: failed to reset group list!\n"); free(gids_saved); return -1; } /* this will remove any duplicates gids in the list and update the group counter */ remove_duplicate_gids( grpcnt, groups ); free(gids_saved); return ret; }
int ckpt_restore_cred(ckpt_desc_t desc) { int ret = 0; ckpt_cred_t cred; gid_t *groups = NULL; const struct cred *curr_cred = current->cred; log_restore_cred("restoring cred ..."); if (ckpt_read(desc, &cred, sizeof(ckpt_cred_t)) != sizeof(ckpt_cred_t)) { log_err("failed to get cred"); return -EIO; } if (cred.ngroups > NGROUPS_MAX) { log_err("ngroups (%d) > NGROUPS_MAX", cred.ngroups); return -EINVAL; } if (cred.ngroups > 0) { int i; size_t size = cred.ngroups * sizeof(gid_t); struct group_info *gi = curr_cred->group_info; groups = vmalloc(size); if (!groups) { log_err("no memory"); return -ENOMEM; } if (ckpt_read(desc, groups, size) != size) { log_err("failed to get groups"); ret = -EIO; goto out; } for (i = 0; i < cred.ngroups; i++) { if (!groups_search(gi, groups[i])) { mm_segment_t fs = get_fs(); set_fs(KERNEL_DS); ret = sys_setgroups(cred.ngroups, groups); set_fs(fs); if (ret < 0) { log_err("failed to set groups"); goto out; } break; } } } current->gpid = cred.gpid; ret = sys_setresgid(cred.gid, cred.egid, cred.sgid); if (ret < 0) { log_err("failed to restore gid"); goto out; } ret = sys_setresuid(cred.uid, cred.euid, cred.suid); if (ret < 0) { log_err("failed to restore uid"); goto out; } if ((curr_cred->euid == curr_cred->uid) && (curr_cred->egid == curr_cred->gid)) set_dumpable(current->mm, 1); else set_dumpable(current->mm, *compat_suid_dumpable); log_restore_pos(desc); out: if (groups) vfree(groups); return ret; }
int winbind_initgroups(char *user, gid_t gid) { gid_t *tgr, *groups = NULL; int result; /* Call normal initgroups if we are a local user */ if (!strchr(user, *lp_winbind_separator())) { return initgroups(user, gid); } result = wb_getgroups(user, &groups); DEBUG(10,("winbind_getgroups: %s: result = %s\n", user, result == -1 ? "FAIL" : "SUCCESS")); if (result != -1) { int ngroups = result, i; BOOL is_member = False; /* Check to see if the passed gid is already in the list */ for (i = 0; i < ngroups; i++) { if (groups[i] == gid) { is_member = True; } } /* Add group to list if necessary */ if (!is_member) { tgr = (gid_t *)Realloc(groups, sizeof(gid_t) * ngroups + 1); if (!tgr) { errno = ENOMEM; result = -1; goto done; } else groups = tgr; groups[ngroups] = gid; ngroups++; } /* Set the groups */ if (sys_setgroups(ngroups, groups) == -1) { errno = EPERM; result = -1; goto done; } } else { /* The call failed. Set errno to something so we don't get a bogus value from the last failed system call. */ errno = EIO; } /* Free response data if necessary */ done: SAFE_FREE(groups); return result; }
/* Note: it is necessary to treat gidsetsize as an unsigned int, * with the corresponding cast to a signed int to insure that the * proper conversion (sign extension) between the register representation of a signed int (msr in 32-bit mode) * and the register representation of a signed int (msr in 64-bit mode) is performed. */ asmlinkage long compat_sys_setgroups(u32 gidsetsize, gid_t __user *grouplist) { return sys_setgroups((int)gidsetsize, grouplist); }
int cr_load_creds(cr_rstrt_proc_req_t *proc_req) { cr_errbuf_t *eb = proc_req->req->errbuf; struct cr_context_creds cf_creds; int retval; gid_t *groups = NULL; CR_KTRACE_HIGH_LVL("%d: Restoring credentials", current->pid); retval = cr_kread(eb, proc_req->file, &cf_creds, sizeof(cf_creds)); if (retval < sizeof(cf_creds)) { CR_ERR_PROC_REQ(proc_req, "credentials: read returned %d", retval); goto out; } if (cf_creds.ngroups > CR_NGROUPS_MAX) { CR_ERR_PROC_REQ(proc_req, "Invalid supplemental group count (%d)", (int)cf_creds.ngroups); retval = -EINVAL; goto out; } else if (cf_creds.ngroups) { size_t sizeof_groups = cf_creds.ngroups*sizeof(gid_t); groups = vmalloc(sizeof_groups); retval = -ENOMEM; if (groups == NULL) { goto out; } retval = cr_kread(eb, proc_req->file, groups, sizeof_groups); if (retval < sizeof_groups) { CR_ERR_PROC_REQ(proc_req, "groups: read returned %d", retval); goto out_vfree; } } #if CR_RESTORE_IDS if (cf_creds.ngroups) { #if CR_HAVE_GROUP_INFO || defined(CR_KCODE_groups_search) || !defined(CR_KCODE_supplemental_group_member) cr_group_info_t gi = cr_current_groups(); #endif int i; /* Search for any required "expansion" of the group set */ for (i = 0; i < cf_creds.ngroups; ++i) { gid_t g = groups[i]; int gid_ok = 0; /* Assume no match for this gid */ #if defined(CR_KCODE_groups_search) gid_ok = groups_search((struct group_info *)gi, g); // search is const, but not declared as such #elif defined(CR_KCODE_supplemental_group_member) gid_ok = supplemental_group_member(g); #else /* Just in case groups_search() or supplemental_group_member() * is not found (each was static in some kernels) */ int j; for (j = 0; j < gi->ngroups; ++j) { if (g == CR_GROUP_AT(gi, j)) { gid_ok = 1; /* OK, g is in the existing set */ break; } } #endif if (!gid_ok) { /* If we reach here then we've seen a supplemental group in the * saved context, which is not present in the current list. * The set_groups() call checks permissions for us. If we fail, * then we must not have enough credentials. */ mm_segment_t oldfs = get_fs(); set_fs(KERNEL_DS); retval = sys_setgroups(cf_creds.ngroups, groups); set_fs(oldfs); if (retval < 0) { CR_ERR_PROC_REQ(proc_req, "Failed to restore supplemental group(s)."); goto out_vfree; } CR_KTRACE_LOW_LVL("Restored %d supplemental group(s)", cf_creds.ngroups); break; /* no need to continue the i loop */ } } #if CR_HAVE_GROUP_INFO (void)cr_insert_object(proc_req->req->map, (void *)cf_creds.group_info, (void *)gi, GFP_KERNEL); #endif } #if CR_HAVE_GROUP_INFO else { // NOTE: requires restore order match save order struct group_info *found_group_info = NULL; if (!cr_find_object(proc_req->req->map, (void *)cf_creds.group_info, (void **)&found_group_info)) { // Nothing to do } else if (found_group_info != cr_current_groups()) { // validation and sort were done previously, but are not worth avoiding set_current_groups(found_group_info); CR_KTRACE_LOW_LVL("Reuse cached group_info %p", found_group_info); } else { CR_KTRACE_LOW_LVL("Cached group_info == current"); } } #endif { cr_cred_t my_cred = cr_current_cred(); /* The set_setresgid() call checks permissions for us, always OK if no change . */ retval = sys_setresgid(cf_creds.gid, cf_creds.egid, cf_creds.sgid); if (retval < 0) { CR_ERR_PROC_REQ(proc_req, "Failed to restore real/effective/saved gids."); goto out_vfree; } CR_KTRACE_LOW_LVL("Restored gids"); /* The set_setresuid() call checks permissions for us, always OK if no change . */ retval = sys_setresuid(cf_creds.uid, cf_creds.euid, cf_creds.suid); if (retval < 0) { CR_ERR_PROC_REQ(proc_req, "Failed to restore real/effective/saved uids."); goto out_vfree; } CR_KTRACE_LOW_LVL("Restored uids"); /* * sys_setresuid sets current->mm->dumpable to suid_dumpable if the * call was successful. This can have some weird side-effects on * restarted jobs, like /proc/self/fd will not be accessable. * * We should probably save this flag and restore it if we ever * support real setuid checkpoints from the user, but for now we'll * just set the flag to 1 if the user had permission to restore their * credentials in the first place. This should mimic the behavior * of exec. * * Set the dumpable flag for the process, taken from 2.6.22 fs/exec.c */ if (my_cred->euid == my_cred->uid && my_cred->egid == my_cred->gid) { cr_set_dumpable(current->mm, 1); } else { cr_set_dumpable(current->mm, cr_suid_dumpable); } } #endif /* CR_RESTORE_IDS */ out_vfree: vfree(groups); out: return retval; }