/* ARGSUSED */ int sys_setreuid(struct proc *p, void *v, register_t *retval) { struct sys_setreuid_args /* { syscallarg(uid_t) ruid; syscallarg(uid_t) euid; } */ *uap = v; struct pcred *pc = p->p_cred; struct sys_setresuid_args sresuidargs; uid_t ruid, euid; ruid = SCARG(&sresuidargs, ruid) = SCARG(uap, ruid); euid = SCARG(&sresuidargs, euid) = SCARG(uap, euid); /* * The saved uid presents a bit of a dilemma, as it did not * exist when setreuid(2) was conceived. We only set the saved * uid when the real uid is specified and either its value would * change, or where the saved and effective uids are different. */ if (ruid != (uid_t)-1 && (ruid != pc->p_ruid || pc->p_svuid != (euid != (uid_t)-1 ? euid : pc->pc_ucred->cr_uid))) SCARG(&sresuidargs, suid) = ruid; else SCARG(&sresuidargs, suid) = (uid_t)-1; return (sys_setresuid(p, &sresuidargs, retval)); }
asmlinkage long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid) { long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid), low2highuid(suid)); /* avoid REGPARM breakage on x86: */ asmlinkage_protect(3, ret, ruid, euid, suid); return ret; }
SYSCALL_DEFINE3(setresuid16, old_uid_t, ruid, old_uid_t, euid, old_uid_t, suid) { long ret = sys_setresuid(low2highuid(ruid), low2highuid(euid), low2highuid(suid)); /* avoid REGPARM breakage on x86: */ asmlinkage_protect(3, ret, ruid, euid, suid); return ret; }
asmlinkage long sys_setresuid16(old_uid_t ruid, old_uid_t euid, old_uid_t suid) { return sys_setresuid(low2highuid(ruid), low2highuid(euid), low2highuid(suid)); }
asmlinkage long sys32_setresuid16(u16 ruid, u16 euid, u16 suid) { return sys_setresuid(low2highuid(ruid), low2highuid(euid), low2highuid(suid)); }
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 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; }
static int restore_creds(CredsEntry *ce) { int b, i, ret; struct cap_header hdr; struct cap_data data[_LINUX_CAPABILITY_U32S_3]; /* * We're still root here and thus can do it without failures. */ /* * First -- set the SECURE_NO_SETUID_FIXUP bit not to * lose caps bits when changing xids. */ ret = sys_prctl(PR_SET_SECUREBITS, 1 << SECURE_NO_SETUID_FIXUP, 0, 0, 0); if (ret) { pr_err("Unable to set SECURE_NO_SETUID_FIXUP: %d\n", ret); return -1; } /* * Second -- restore xids. Since we still have the CAP_SETUID * capability nothing should fail. But call the setfsXid last * to override the setresXid settings. */ ret = sys_setresuid(ce->uid, ce->euid, ce->suid); if (ret) { pr_err("Unable to set real, effective and saved user ID: %d\n", ret); return -1; } sys_setfsuid(ce->fsuid); if (sys_setfsuid(-1) != ce->fsuid) { pr_err("Unable to set fsuid\n"); return -1; } ret = sys_setresgid(ce->gid, ce->egid, ce->sgid); if (ret) { pr_err("Unable to set real, effective and saved group ID: %d\n", ret); return -1; } sys_setfsgid(ce->fsgid); if (sys_setfsgid(-1) != ce->fsgid) { pr_err("Unable to set fsgid\n"); return -1; } /* * Third -- restore securebits. We don't need them in any * special state any longer. */ ret = sys_prctl(PR_SET_SECUREBITS, ce->secbits, 0, 0, 0); if (ret) { pr_err("Unable to set PR_SET_SECUREBITS: %d\n", ret); return -1; } /* * Fourth -- trim bset. This can only be done while * having the CAP_SETPCAP capablity. */ for (b = 0; b < CR_CAP_SIZE; b++) { for (i = 0; i < 32; i++) { if (b * 32 + i > cap_last_cap) break; if (ce->cap_bnd[b] & (1 << i)) /* already set */ continue; ret = sys_prctl(PR_CAPBSET_DROP, i + b * 32, 0, 0, 0); if (ret) { pr_err("Unable to drop capability %d: %d\n", i + b * 32, ret); return -1; } } } /* * Fifth -- restore caps. Nothing but cap bits are changed * at this stage, so just do it. */ hdr.version = _LINUX_CAPABILITY_VERSION_3; hdr.pid = 0; BUILD_BUG_ON(_LINUX_CAPABILITY_U32S_3 != CR_CAP_SIZE); for (i = 0; i < CR_CAP_SIZE; i++) { data[i].eff = ce->cap_eff[i]; data[i].prm = ce->cap_prm[i]; data[i].inh = ce->cap_inh[i]; } ret = sys_capset(&hdr, data); if (ret) { pr_err("Unable to restore capabilities: %d\n", ret); return -1; } return 0; }