mirror of
https://github.com/PowerShell/openssh-portable.git
synced 2025-09-01 23:28:33 +02:00
OpenSSh privilege separation model - http://www.citi.umich.edu/u/provos/ssh/privsep.html Posix_spawn is implemented in POSIX adapter as an alternative to fork() that is heavily used in Privilege separation. Additional state info is added to sshd to accommodate distinguishing the various modes (privileged monitor, unprivileged child, authenticated child). Required service state (like config and host keys) is transmitted over pipes from monitor to child processes. Changes to installation scripts and tests to accomodate new architectural changes
221 lines
5.8 KiB
C
221 lines
5.8 KiB
C
/* $OpenBSD: readpass.c,v 1.51 2015/12/11 00:20:04 mmcc Exp $ */
|
|
/*
|
|
* Copyright (c) 2001 Markus Friedl. All rights reserved.
|
|
*
|
|
* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions
|
|
* are met:
|
|
* 1. Redistributions of source code must retain the above copyright
|
|
* notice, this list of conditions and the following disclaimer.
|
|
* 2. Redistributions in binary form must reproduce the above copyright
|
|
* notice, this list of conditions and the following disclaimer in the
|
|
* documentation and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
|
|
* IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
|
|
* OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
|
|
* IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
|
|
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
|
|
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
|
*/
|
|
|
|
#include "includes.h"
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/wait.h>
|
|
|
|
#include <errno.h>
|
|
#include <fcntl.h>
|
|
#ifdef HAVE_PATHS_H
|
|
# include <paths.h>
|
|
#endif
|
|
#include <signal.h>
|
|
#include <stdarg.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <unistd.h>
|
|
|
|
#include "xmalloc.h"
|
|
#include "misc.h"
|
|
#include "pathnames.h"
|
|
#include "log.h"
|
|
#include "ssh.h"
|
|
#include "uidswap.h"
|
|
|
|
static char *
|
|
ssh_askpass(char *askpass, const char *msg)
|
|
{
|
|
pid_t pid, ret;
|
|
size_t len;
|
|
char *pass;
|
|
int p[2], status;
|
|
char buf[1024];
|
|
void (*osigchld)(int);
|
|
|
|
if (fflush(stdout) != 0)
|
|
error("ssh_askpass: fflush: %s", strerror(errno));
|
|
if (askpass == NULL)
|
|
fatal("internal error: askpass undefined");
|
|
if (pipe(p) < 0) {
|
|
error("ssh_askpass: pipe: %s", strerror(errno));
|
|
return NULL;
|
|
}
|
|
osigchld = signal(SIGCHLD, SIG_DFL);
|
|
fcntl(p[0], F_SETFD, FD_CLOEXEC);
|
|
fcntl(p[1], F_SETFD, FD_CLOEXEC);
|
|
#ifdef FORK_NOT_SUPPORTED
|
|
{
|
|
posix_spawn_file_actions_t actions;
|
|
pid = -1;
|
|
if (posix_spawn_file_actions_init(&actions) != 0 ||
|
|
posix_spawn_file_actions_adddup2(&actions, p[1], STDOUT_FILENO) != 0 ) {
|
|
error("posix_spawn initialization failed");
|
|
signal(SIGCHLD, osigchld);
|
|
return NULL;
|
|
} else {
|
|
char* spawn_argv[2];
|
|
spawn_argv[0] = askpass;
|
|
spawn_argv[1] = NULL;
|
|
if (posix_spawn(&pid, spawn_argv[0], &actions, NULL, spawn_argv, NULL) != 0) {
|
|
posix_spawn_file_actions_destroy(&actions);
|
|
error("ssh_askpass: posix_spawn: %s", strerror(errno));
|
|
signal(SIGCHLD, osigchld);
|
|
return NULL;
|
|
}
|
|
posix_spawn_file_actions_destroy(&actions);
|
|
}
|
|
|
|
}
|
|
#else
|
|
if ((pid = fork()) < 0) {
|
|
error("ssh_askpass: fork: %s", strerror(errno));
|
|
signal(SIGCHLD, osigchld);
|
|
return NULL;
|
|
}
|
|
if (pid == 0) {
|
|
permanently_drop_suid(getuid());
|
|
close(p[0]);
|
|
if (dup2(p[1], STDOUT_FILENO) < 0)
|
|
fatal("ssh_askpass: dup2: %s", strerror(errno));
|
|
execlp(askpass, askpass, msg, (char *)NULL);
|
|
fatal("ssh_askpass: exec(%s): %s", askpass, strerror(errno));
|
|
}
|
|
#endif
|
|
close(p[1]);
|
|
|
|
len = 0;
|
|
do {
|
|
ssize_t r = read(p[0], buf + len, sizeof(buf) - 1 - len);
|
|
|
|
if (r == -1 && errno == EINTR)
|
|
continue;
|
|
if (r <= 0)
|
|
break;
|
|
len += r;
|
|
} while (sizeof(buf) - 1 - len > 0);
|
|
buf[len] = '\0';
|
|
|
|
close(p[0]);
|
|
while ((ret = waitpid(pid, &status, 0)) < 0)
|
|
if (errno != EINTR)
|
|
break;
|
|
signal(SIGCHLD, osigchld);
|
|
if (ret == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0) {
|
|
explicit_bzero(buf, sizeof(buf));
|
|
return NULL;
|
|
}
|
|
|
|
buf[strcspn(buf, "\r\n")] = '\0';
|
|
pass = xstrdup(buf);
|
|
explicit_bzero(buf, sizeof(buf));
|
|
return pass;
|
|
}
|
|
|
|
/*
|
|
* Reads a passphrase from /dev/tty with echo turned off/on. Returns the
|
|
* passphrase (allocated with xmalloc). Exits if EOF is encountered. If
|
|
* RP_ALLOW_STDIN is set, the passphrase will be read from stdin if no
|
|
* tty is available
|
|
*/
|
|
char *
|
|
read_passphrase(const char *prompt, int flags)
|
|
{
|
|
char *askpass = NULL, *ret, buf[1024];
|
|
int rppflags, use_askpass = 0, ttyfd;
|
|
|
|
rppflags = (flags & RP_ECHO) ? RPP_ECHO_ON : RPP_ECHO_OFF;
|
|
if (flags & RP_USE_ASKPASS)
|
|
use_askpass = 1;
|
|
else if (flags & RP_ALLOW_STDIN) {
|
|
if (!isatty(STDIN_FILENO)) {
|
|
debug("read_passphrase: stdin is not a tty");
|
|
use_askpass = 1;
|
|
}
|
|
} else {
|
|
rppflags |= RPP_REQUIRE_TTY;
|
|
ttyfd = open(_PATH_TTY, O_RDWR);
|
|
if (ttyfd >= 0)
|
|
close(ttyfd);
|
|
else {
|
|
debug("read_passphrase: can't open %s: %s", _PATH_TTY,
|
|
strerror(errno));
|
|
use_askpass = 1;
|
|
}
|
|
}
|
|
|
|
if ((flags & RP_USE_ASKPASS) && getenv("DISPLAY") == NULL)
|
|
return (flags & RP_ALLOW_EOF) ? NULL : xstrdup("");
|
|
|
|
if (use_askpass && getenv("DISPLAY")) {
|
|
if (getenv(SSH_ASKPASS_ENV))
|
|
askpass = getenv(SSH_ASKPASS_ENV);
|
|
else
|
|
askpass = _PATH_SSH_ASKPASS_DEFAULT;
|
|
if ((ret = ssh_askpass(askpass, prompt)) == NULL)
|
|
if (!(flags & RP_ALLOW_EOF))
|
|
return xstrdup("");
|
|
return ret;
|
|
}
|
|
|
|
if (readpassphrase(prompt, buf, sizeof buf, rppflags) == NULL) {
|
|
if (flags & RP_ALLOW_EOF)
|
|
return NULL;
|
|
return xstrdup("");
|
|
}
|
|
|
|
ret = xstrdup(buf);
|
|
explicit_bzero(buf, sizeof(buf));
|
|
return ret;
|
|
}
|
|
|
|
int
|
|
ask_permission(const char *fmt, ...)
|
|
{
|
|
va_list args;
|
|
char *p, prompt[1024];
|
|
int allowed = 0;
|
|
|
|
va_start(args, fmt);
|
|
vsnprintf(prompt, sizeof(prompt), fmt, args);
|
|
va_end(args);
|
|
|
|
p = read_passphrase(prompt, RP_USE_ASKPASS|RP_ALLOW_EOF);
|
|
if (p != NULL) {
|
|
/*
|
|
* Accept empty responses and responses consisting
|
|
* of the word "yes" as affirmative.
|
|
*/
|
|
if (*p == '\0' || *p == '\n' ||
|
|
strcasecmp(p, "yes") == 0)
|
|
allowed = 1;
|
|
free(p);
|
|
}
|
|
|
|
return (allowed);
|
|
}
|