void __libc_init_secure (void) { if (__libc_enable_secure_decided == 0) __libc_enable_secure = (__geteuid () != __getuid () || __getegid () != __getgid ()); }
/* Return any pending signal or wait for one for the given time. */ int __sigqueue (pid_t pid, int sig, const union sigval val) { siginfo_t info; /* First, clear the siginfo_t structure, so that we don't pass our stack content to other tasks. */ memset (&info, 0, sizeof (siginfo_t)); /* We must pass the information about the data in a siginfo_t value. */ info.si_signo = sig; info.si_code = SI_QUEUE; info.si_pid = __getpid (); info.si_uid = __getuid (); info.si_value = val; return INLINE_SYSCALL (rt_sigqueueinfo, 3, pid, sig, &info); }
int faccessat (int fd, const char *file, int mode, int flag) { if (flag & ~(AT_SYMLINK_NOFOLLOW | AT_EACCESS)) return INLINE_SYSCALL_ERROR_RETURN_VALUE (EINVAL); if ((flag == 0 || ((flag & ~AT_EACCESS) == 0 && ! __libc_enable_secure))) return INLINE_SYSCALL (faccessat, 3, fd, file, mode); struct stat64 stats; if (__fxstatat64 (_STAT_VER, fd, file, &stats, flag & AT_SYMLINK_NOFOLLOW)) return -1; mode &= (X_OK | W_OK | R_OK); /* Clear any bogus bits. */ #if R_OK != S_IROTH || W_OK != S_IWOTH || X_OK != S_IXOTH # error Oops, portability assumptions incorrect. #endif if (mode == F_OK) return 0; /* The file exists. */ uid_t uid = (flag & AT_EACCESS) ? __geteuid () : __getuid (); /* The super-user can read and write any file, and execute any file that anyone can execute. */ if (uid == 0 && ((mode & X_OK) == 0 || (stats.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)))) return 0; int granted = (uid == stats.st_uid ? (unsigned int) (stats.st_mode & (mode << 6)) >> 6 : (stats.st_gid == ((flag & AT_EACCESS) ? __getegid () : __getgid ()) || __group_member (stats.st_gid)) ? (unsigned int) (stats.st_mode & (mode << 3)) >> 3 : (stats.st_mode & mode)); if (granted == mode) return 0; return INLINE_SYSCALL_ERROR_RETURN_VALUE (EACCES); }
/* Spawn a new process executing PATH with the attributes describes in *ATTRP. Before running the process perform the actions described in FILE-ACTIONS. */ int __spawni (pid_t *pid, const char *file, const posix_spawn_file_actions_t *file_actions, const posix_spawnattr_t *attrp, char *const argv[], char *const envp[], int xflags) { pid_t new_pid; char *path, *p, *name; size_t len; size_t pathlen; /* Do this once. */ short int flags = attrp == NULL ? 0 : attrp->__flags; /* Generate the new process. */ if ((flags & POSIX_SPAWN_USEVFORK) != 0 /* If no major work is done, allow using vfork. Note that we might perform the path searching. But this would be done by a call to execvp(), too, and such a call must be OK according to POSIX. */ || ((flags & (POSIX_SPAWN_SETSIGMASK | POSIX_SPAWN_SETSIGDEF | POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER | POSIX_SPAWN_SETPGROUP | POSIX_SPAWN_RESETIDS)) == 0 && file_actions == NULL)) new_pid = __vfork (); else new_pid = __fork (); if (new_pid != 0) { if (new_pid < 0) return errno; /* The call was successful. Store the PID if necessary. */ if (pid != NULL) *pid = new_pid; return 0; } /* Set signal mask. */ if ((flags & POSIX_SPAWN_SETSIGMASK) != 0 && __sigprocmask (SIG_SETMASK, &attrp->__ss, NULL) != 0) _exit (SPAWN_ERROR); /* Set signal default action. */ if ((flags & POSIX_SPAWN_SETSIGDEF) != 0) { /* We have to iterate over all signals. This could possibly be done better but it requires system specific solutions since the sigset_t data type can be very different on different architectures. */ int sig; struct sigaction sa; memset (&sa, '\0', sizeof (sa)); sa.sa_handler = SIG_DFL; for (sig = 1; sig <= _NSIG; ++sig) if (__sigismember (&attrp->__sd, sig) != 0 && __sigaction (sig, &sa, NULL) != 0) _exit (SPAWN_ERROR); } #ifdef _POSIX_PRIORITY_SCHEDULING /* Set the scheduling algorithm and parameters. */ if ((flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) == POSIX_SPAWN_SETSCHEDPARAM) { if (__sched_setparam (0, &attrp->__sp) == -1) _exit (SPAWN_ERROR); } else if ((flags & POSIX_SPAWN_SETSCHEDULER) != 0) { if (__sched_setscheduler (0, attrp->__policy, &attrp->__sp) == -1) _exit (SPAWN_ERROR); } #endif /* Set the process group ID. */ if ((flags & POSIX_SPAWN_SETPGROUP) != 0 && __setpgid (0, attrp->__pgrp) != 0) _exit (SPAWN_ERROR); /* Set the effective user and group IDs. */ if ((flags & POSIX_SPAWN_RESETIDS) != 0 && (local_seteuid (__getuid ()) != 0 || local_setegid (__getgid ()) != 0)) _exit (SPAWN_ERROR); /* Execute the file actions. */ if (file_actions != NULL) { int cnt; struct rlimit64 fdlimit; bool have_fdlimit = false; for (cnt = 0; cnt < file_actions->__used; ++cnt) { struct __spawn_action *action = &file_actions->__actions[cnt]; switch (action->tag) { case spawn_do_close: if (close_not_cancel (action->action.close_action.fd) != 0) { if (! have_fdlimit) { getrlimit64 (RLIMIT_NOFILE, &fdlimit); have_fdlimit = true; } /* Only signal errors for file descriptors out of range. */ if (action->action.close_action.fd < 0 || action->action.close_action.fd >= fdlimit.rlim_cur) /* Signal the error. */ _exit (SPAWN_ERROR); } break; case spawn_do_open: { int new_fd = open_not_cancel (action->action.open_action.path, action->action.open_action.oflag | O_LARGEFILE, action->action.open_action.mode); if (new_fd == -1) /* The `open' call failed. */ _exit (SPAWN_ERROR); /* Make sure the desired file descriptor is used. */ if (new_fd != action->action.open_action.fd) { if (__dup2 (new_fd, action->action.open_action.fd) != action->action.open_action.fd) /* The `dup2' call failed. */ _exit (SPAWN_ERROR); if (close_not_cancel (new_fd) != 0) /* The `close' call failed. */ _exit (SPAWN_ERROR); } } break; case spawn_do_dup2: if (__dup2 (action->action.dup2_action.fd, action->action.dup2_action.newfd) != action->action.dup2_action.newfd) /* The `dup2' call failed. */ _exit (SPAWN_ERROR); break; } } } if ((xflags & SPAWN_XFLAGS_USE_PATH) == 0 || strchr (file, '/') != NULL) { /* The FILE parameter is actually a path. */ __execve (file, argv, envp); maybe_script_execute (file, argv, envp, xflags); /* Oh, oh. `execve' returns. This is bad. */ _exit (SPAWN_ERROR); } /* We have to search for FILE on the path. */ path = getenv ("PATH"); if (path == NULL) { /* There is no `PATH' in the environment. The default search path is the current directory followed by the path `confstr' returns for `_CS_PATH'. */ len = confstr (_CS_PATH, (char *) NULL, 0); path = (char *) __alloca (1 + len); path[0] = ':'; (void) confstr (_CS_PATH, path + 1, len); } len = strlen (file) + 1; pathlen = strlen (path); name = __alloca (pathlen + len + 1); /* Copy the file name at the top. */ name = (char *) memcpy (name + pathlen + 1, file, len); /* And add the slash. */ *--name = '/'; p = path; do { char *startp; path = p; p = __strchrnul (path, ':'); if (p == path) /* Two adjacent colons, or a colon at the beginning or the end of `PATH' means to search the current directory. */ startp = name + 1; else startp = (char *) memcpy (name - (p - path), path, p - path); /* Try to execute this name. If it works, execv will not return. */ __execve (startp, argv, envp); maybe_script_execute (startp, argv, envp, xflags); switch (errno) { case EACCES: case ENOENT: case ESTALE: case ENOTDIR: /* Those errors indicate the file is missing or not executable by us, in which case we want to just try the next path directory. */ break; default: /* Some other error means we found an executable file, but something went wrong executing it; return the error to our caller. */ _exit (SPAWN_ERROR); } } while (*p++ != '\0'); /* Return with an error. */ _exit (SPAWN_ERROR); }
/* Change the ownership and access permission of the slave pseudo terminal associated with the master pseudo terminal specified by FD. */ int grantpt (int fd) { int retval = -1; #ifdef PATH_MAX char _buf[PATH_MAX]; #else char _buf[512]; #endif char *buf = _buf; struct stat64 st; if (__glibc_unlikely (pts_name (fd, &buf, sizeof (_buf), &st))) { int save_errno = errno; /* Check, if the file descriptor is valid. pts_name returns the wrong errno number, so we cannot use that. */ if (__libc_fcntl (fd, F_GETFD) == -1 && errno == EBADF) return -1; /* If the filedescriptor is no TTY, grantpt has to set errno to EINVAL. */ if (save_errno == ENOTTY) __set_errno (EINVAL); else __set_errno (save_errno); return -1; } /* Make sure that we own the device. */ uid_t uid = __getuid (); if (st.st_uid != uid) { if (__chown (buf, uid, st.st_gid) < 0) goto helper; } static int tty_gid = -1; if (__glibc_unlikely (tty_gid == -1)) { char *grtmpbuf; struct group grbuf; size_t grbuflen = __sysconf (_SC_GETGR_R_SIZE_MAX); struct group *p; /* Get the group ID of the special `tty' group. */ if (grbuflen == (size_t) -1L) /* `sysconf' does not support _SC_GETGR_R_SIZE_MAX. Try a moderate value. */ grbuflen = 1024; grtmpbuf = (char *) __alloca (grbuflen); __getgrnam_r (TTY_GROUP, &grbuf, grtmpbuf, grbuflen, &p); if (p != NULL) tty_gid = p->gr_gid; } gid_t gid = tty_gid == -1 ? __getgid () : tty_gid; /* Make sure the group of the device is that special group. */ if (st.st_gid != gid) { if (__chown (buf, uid, gid) < 0) goto helper; } /* Make sure the permission mode is set to readable and writable by the owner, and writable by the group. */ if ((st.st_mode & ACCESSPERMS) != (S_IRUSR|S_IWUSR|S_IWGRP)) { if (__chmod (buf, S_IRUSR|S_IWUSR|S_IWGRP) < 0) goto helper; } retval = 0; goto cleanup; /* We have to use the helper program if it is available. */ helper:; #ifdef HAVE_PT_CHOWN pid_t pid = __fork (); if (pid == -1) goto cleanup; else if (pid == 0) { /* Disable core dumps. */ struct rlimit rl = { 0, 0 }; __setrlimit (RLIMIT_CORE, &rl); /* We pass the master pseudo terminal as file descriptor PTY_FILENO. */ if (fd != PTY_FILENO) if (__dup2 (fd, PTY_FILENO) < 0) _exit (FAIL_EBADF); # ifdef CLOSE_ALL_FDS CLOSE_ALL_FDS (); # endif execle (_PATH_PT_CHOWN, basename (_PATH_PT_CHOWN), NULL, NULL); _exit (FAIL_EXEC); } else { int w; if (__waitpid (pid, &w, 0) == -1) goto cleanup; if (!WIFEXITED (w)) __set_errno (ENOEXEC); else switch (WEXITSTATUS (w)) { case 0: retval = 0; break; case FAIL_EBADF: __set_errno (EBADF); break; case FAIL_EINVAL: __set_errno (EINVAL); break; case FAIL_EACCES: __set_errno (EACCES); break; case FAIL_EXEC: __set_errno (ENOEXEC); break; case FAIL_ENOMEM: __set_errno (ENOMEM); break; default: assert(! "getpt: internal error: invalid exit code from pt_chown"); } } #endif cleanup: if (buf != _buf) free (buf); return retval; }
/* Function used in the clone call to setup the signals mask, posix_spawn attributes, and file actions. It run on its own stack (provided by the posix_spawn call). */ static int __spawni_child (void *arguments) { struct posix_spawn_args *args = arguments; const posix_spawnattr_t *restrict attr = args->attr; const posix_spawn_file_actions_t *file_actions = args->fa; int p = args->pipe[1]; int ret; close_not_cancel (args->pipe[0]); /* The child must ensure that no signal handler are enabled because it shared memory with parent, so the signal disposition must be either SIG_DFL or SIG_IGN. It does by iterating over all signals and although it could possibly be more optimized (by tracking which signal potentially have a signal handler), it might requires system specific solutions (since the sigset_t data type can be very different on different architectures). */ struct sigaction sa; memset (&sa, '\0', sizeof (sa)); sigset_t hset; __sigprocmask (SIG_BLOCK, 0, &hset); for (int sig = 1; sig < _NSIG; ++sig) { if ((attr->__flags & POSIX_SPAWN_SETSIGDEF) && sigismember (&attr->__sd, sig)) { sa.sa_handler = SIG_DFL; } else if (sigismember (&hset, sig)) { if (__nptl_is_internal_signal (sig)) sa.sa_handler = SIG_IGN; else { __libc_sigaction (sig, 0, &sa); if (sa.sa_handler == SIG_IGN) continue; sa.sa_handler = SIG_DFL; } } else continue; __libc_sigaction (sig, &sa, 0); } #ifdef _POSIX_PRIORITY_SCHEDULING /* Set the scheduling algorithm and parameters. */ if ((attr->__flags & (POSIX_SPAWN_SETSCHEDPARAM | POSIX_SPAWN_SETSCHEDULER)) == POSIX_SPAWN_SETSCHEDPARAM) { if ((ret = __sched_setparam (0, &attr->__sp)) == -1) goto fail; } else if ((attr->__flags & POSIX_SPAWN_SETSCHEDULER) != 0) { if ((ret = __sched_setscheduler (0, attr->__policy, &attr->__sp)) == -1) goto fail; } #endif /* Set the process group ID. */ if ((attr->__flags & POSIX_SPAWN_SETPGROUP) != 0 && (ret = __setpgid (0, attr->__pgrp)) != 0) goto fail; /* Set the effective user and group IDs. */ if ((attr->__flags & POSIX_SPAWN_RESETIDS) != 0 && ((ret = local_seteuid (__getuid ())) != 0 || (ret = local_setegid (__getgid ())) != 0)) goto fail; /* Execute the file actions. */ if (file_actions != 0) { int cnt; struct rlimit64 fdlimit; bool have_fdlimit = false; for (cnt = 0; cnt < file_actions->__used; ++cnt) { struct __spawn_action *action = &file_actions->__actions[cnt]; /* Dup the pipe fd onto an unoccupied one to avoid any file operation to clobber it. */ if ((action->action.close_action.fd == p) || (action->action.open_action.fd == p) || (action->action.dup2_action.fd == p)) { if ((ret = __dup (p)) < 0) goto fail; p = ret; } switch (action->tag) { case spawn_do_close: if ((ret = close_not_cancel (action->action.close_action.fd)) != 0) { if (!have_fdlimit) { __getrlimit64 (RLIMIT_NOFILE, &fdlimit); have_fdlimit = true; } /* Signal errors only for file descriptors out of range. */ if (action->action.close_action.fd < 0 || action->action.close_action.fd >= fdlimit.rlim_cur) goto fail; } break; case spawn_do_open: { ret = open_not_cancel (action->action.open_action.path, action->action. open_action.oflag | O_LARGEFILE, action->action.open_action.mode); if (ret == -1) goto fail; int new_fd = ret; /* Make sure the desired file descriptor is used. */ if (ret != action->action.open_action.fd) { if ((ret = __dup2 (new_fd, action->action.open_action.fd)) != action->action.open_action.fd) goto fail; if ((ret = close_not_cancel (new_fd)) != 0) goto fail; } } break; case spawn_do_dup2: if ((ret = __dup2 (action->action.dup2_action.fd, action->action.dup2_action.newfd)) != action->action.dup2_action.newfd) goto fail; break; } } } /* Set the initial signal mask of the child if POSIX_SPAWN_SETSIGMASK is set, otherwise restore the previous one. */ __sigprocmask (SIG_SETMASK, (attr->__flags & POSIX_SPAWN_SETSIGMASK) ? &attr->__ss : &args->oldmask, 0); args->exec (args->file, args->argv, args->envp); /* This is compatibility function required to enable posix_spawn run script without shebang definition for older posix_spawn versions (2.15). */ maybe_script_execute (args); ret = -errno; fail: /* Since sizeof errno < PIPE_BUF, the write is atomic. */ ret = -ret; if (ret) while (write_not_cancel (p, &ret, sizeof ret) < 0) continue; exit (SPAWN_ERROR); }
static int internal_function key_call_keyenvoy (u_long proc, xdrproc_t xdr_arg, char *arg, xdrproc_t xdr_rslt, char *rslt) { XDR xdrargs; XDR xdrrslt; FILE *fargs; FILE *frslt; sigset_t oldmask, mask; union wait status; int pid; int success; uid_t ruid; uid_t euid; static const char MESSENGER[] = "/usr/etc/keyenvoy"; success = 1; sigemptyset (&mask); sigaddset (&mask, SIGCHLD); __sigprocmask (SIG_BLOCK, &mask, &oldmask); /* * We are going to exec a set-uid program which makes our effective uid * zero, and authenticates us with our real uid. We need to make the * effective uid be the real uid for the setuid program, and * the real uid be the effective uid so that we can change things back. */ euid = __geteuid (); ruid = __getuid (); __setreuid (euid, ruid); pid = _openchild (MESSENGER, &fargs, &frslt); __setreuid (ruid, euid); if (pid < 0) { debug ("open_streams"); __sigprocmask (SIG_SETMASK, &oldmask, NULL); return (0); } xdrstdio_create (&xdrargs, fargs, XDR_ENCODE); xdrstdio_create (&xdrrslt, frslt, XDR_DECODE); if (!INTUSE(xdr_u_long) (&xdrargs, &proc) || !(*xdr_arg) (&xdrargs, arg)) { debug ("xdr args"); success = 0; } fclose (fargs); if (success && !(*xdr_rslt) (&xdrrslt, rslt)) { debug ("xdr rslt"); success = 0; } fclose(frslt); wait_again: if (__wait4 (pid, &status, 0, NULL) < 0) { if (errno == EINTR) goto wait_again; debug ("wait4"); if (errno == ECHILD || errno == ESRCH) perror ("wait"); else success = 0; } else if (status.w_retcode) { debug ("wait4 1"); success = 0; } __sigprocmask (SIG_SETMASK, &oldmask, NULL); return success; }
init_secure (void) { __libc_enable_secure = (__geteuid () != __getuid () || __getegid () != __getgid ()); }