qrexec: use PAM directly instead of calling su to setup the session

Instead of calling 'su' to switch the user, use own implementation of
this. Thanks to PAM it's pretty simple. The main reason is to have
control over process waiting for session termination (to call
pam_close_sesion/pam_end). Especially we don't want it to keep std* fds
open, which would prevent qrexec-agent from receiving EOF when one of
them will be closed.
Also, this will preserve QREXEC_AGENT_PID environment variable.

Fixes QubesOS/qubes-issues#2851
这个提交包含在:
Marek Marczykowski-Górecki 2014-10-31 01:07:49 +01:00
父节点 f49042211b
当前提交 3af55c5cb3
找不到此签名对应的密钥
GPG 密钥 ID: 063938BA42CFA724
共有 6 个文件被更改,包括 159 次插入1 次删除

1
debian/control vendored
查看文件

@ -3,6 +3,7 @@ Section: admin
Priority: extra
Maintainer: unman <unman@thirdeyesecurity.org>
Build-Depends:
libpam0g-dev,
libqrexec-utils-dev,
libqubes-rpc-filecopy-dev (>= 3.1.3),
libvchan-xen-dev,

查看文件

@ -1,3 +1,4 @@
etc/pam.d/qrexec
lib/systemd/system/qubes-qrexec-agent.service
usr/bin/qrexec-client-vm
usr/bin/qrexec-fork-server

查看文件

@ -1,7 +1,7 @@
CC=gcc
CFLAGS+=-I. -g -O2 -Wall -Wextra -Werror -pie -fPIC `pkg-config --cflags vchan-$(BACKEND_VMM)`
LDFLAGS=-pie
LDLIBS=`pkg-config --libs vchan-$(BACKEND_VMM)` -lqrexec-utils
LDLIBS=`pkg-config --libs vchan-$(BACKEND_VMM)` -lqrexec-utils -lpam
all: qrexec-agent qrexec-client-vm qrexec-fork-server
qrexec-agent: qrexec-agent.o qrexec-agent-data.o
@ -19,4 +19,5 @@ install:
ln -s ../../bin/qrexec-client-vm $(DESTDIR)/usr/lib/qubes/qrexec_client_vm
install qrexec-fork-server $(DESTDIR)/usr/bin
install qubes-rpc-multiplexer $(DESTDIR)/usr/lib/qubes
install -D -m 0644 qrexec.pam $(DESTDIR)/etc/pam.d/qrexec

查看文件

@ -20,6 +20,8 @@
*/
#define _GNU_SOURCE
#define HAVE_PAM
#include <sys/select.h>
#include <sys/socket.h>
#include <sys/un.h>
@ -35,6 +37,9 @@
#include <grp.h>
#include <sys/stat.h>
#include <assert.h>
#ifdef HAVE_PAM
#include <security/pam_appl.h>
#endif
#include "qrexec.h"
#include <libvchan.h>
#include "libqrexec-utils.h"
@ -65,6 +70,36 @@ void no_colon_in_cmd()
exit(1);
}
#ifdef HAVE_PAM
int pam_conv_callback(int num_msg, const struct pam_message **msg,
struct pam_response **resp, void *appdata_ptr __attribute__((__unused__)))
{
int i;
struct pam_response *resp_array =
calloc(sizeof(struct pam_response), num_msg);
if (resp_array == NULL)
return PAM_BUF_ERR;
for (i=0; i<num_msg; i++) {
if (msg[i]->msg_style == PAM_ERROR_MSG)
fprintf(stderr, "%s", msg[i]->msg);
if (msg[i]->msg_style == PAM_PROMPT_ECHO_OFF ||
msg[i]->msg_style == PAM_PROMPT_ECHO_ON) {
resp_array[i].resp = strdup("");
resp_array[i].resp_retcode = 0;
}
}
*resp = resp_array;
return PAM_SUCCESS;
}
static struct pam_conv conv = {
pam_conv_callback,
NULL
};
#endif
/* Start program requested by dom0 in already prepared process
* (stdin/stdout/stderr already set, etc)
* Called in two cases:
@ -86,6 +121,16 @@ void do_exec(const char *cmd)
{
char buf[strlen(QUBES_RPC_MULTIPLEXER_PATH) + strlen(cmd) - RPC_REQUEST_COMMAND_LEN + 1];
char *realcmd = index(cmd, ':'), *user;
#ifdef HAVE_PAM
int retval, status;
pam_handle_t *pamh=NULL;
struct passwd *pw;
struct passwd pw_copy;
pid_t child, pid;
char **env;
char pid_s[32];
#endif
if (!realcmd)
no_colon_in_cmd();
/* mark end of username and move to command */
@ -103,9 +148,108 @@ void do_exec(const char *cmd)
signal(SIGCHLD, SIG_DFL);
signal(SIGPIPE, SIG_DFL);
#ifdef HAVE_PAM
pw = getpwnam (user);
if (! (pw && pw->pw_name && pw->pw_name[0] && pw->pw_dir && pw->pw_dir[0]
&& pw->pw_passwd)) {
fprintf(stderr, "user %s does not exist", user);
exit(1);
}
/* Make a copy of the password information and point pw at the local
* copy instead. Otherwise, some systems (e.g. Linux) would clobber
* the static data through the getlogin call.
*/
pw_copy = *pw;
pw = &pw_copy;
pw->pw_name = strdup(pw->pw_name);
pw->pw_passwd = strdup(pw->pw_passwd);
pw->pw_dir = strdup(pw->pw_dir);
pw->pw_shell = strdup(pw->pw_shell);
endpwent();
retval = pam_start("qrexec", user, &conv, &pamh);
if (retval != PAM_SUCCESS)
goto error;
retval = pam_authenticate(pamh, 0);
if (retval != PAM_SUCCESS)
goto error;
retval = initgroups(pw->pw_name, pw->pw_gid);
if (retval == -1) {
perror("initgroups");
goto error;
}
retval = pam_setcred(pamh, PAM_ESTABLISH_CRED);
if (retval != PAM_SUCCESS)
goto error;
retval = pam_open_session(pamh, 0);
if (retval != PAM_SUCCESS)
goto error;
/* provide this variable to child process */
snprintf(pid_s, sizeof(pid_s), "QREXEC_AGENT_PID=%d", getppid());
retval = pam_putenv(pamh, pid_s);
if (retval != PAM_SUCCESS)
goto error;
/* FORK HERE */
child = fork ();
switch (child) {
case -1:
goto error;
case 0:
/* child */
if (setgid (pw->pw_gid))
exit(126);
if (setuid (pw->pw_uid))
exit(126);
setsid();
/* This is a copy but don't care to free as we exec later anyways. */
env = pam_getenvlist (pamh);
execle("/bin/sh", "sh", "-c", realcmd, (char*)NULL, env);
exit(127);
default:
/* parent */
/* close std*, so when child process closes them, qrexec-agent will receive EOF */
/* this is the main purpose of this reimplementation of /bin/su... */
close(0);
close(1);
close(2);
}
/* reachable only in parent */
pid = waitpid (child, &status, 0);
if (pid != (pid_t)-1) {
if (WIFSIGNALED (status))
status = WTERMSIG (status) + 128;
else
status = WEXITSTATUS (status);
} else
status = 1;
retval = pam_close_session (pamh, 0);
retval = pam_setcred (pamh, PAM_DELETE_CRED | PAM_SILENT);
if (pam_end(pamh, retval) != PAM_SUCCESS) { /* close Linux-PAM */
pamh = NULL;
exit(1);
}
exit(status);
error:
pam_end(pamh, PAM_ABORT);
exit(1);
#else
execl("/bin/su", "su", "-", user, "-c", realcmd, NULL);
perror("execl");
exit(1);
#endif
}
void handle_vchan_error(const char *op)

9
qrexec/qrexec.pam 普通文件
查看文件

@ -0,0 +1,9 @@
#%PAM-1.0
auth sufficient pam_rootok.so
auth substack system-auth
auth include postlogin
account sufficient pam_succeed_if.so uid = 0 use_uid quiet
account include system-auth
password include system-auth
session include system-auth
session include postlogin

查看文件

@ -157,6 +157,7 @@ BuildRequires: xen-devel
BuildRequires: libX11-devel
BuildRequires: qubes-utils-devel >= 3.1.3
BuildRequires: qubes-libvchan-%{backend_vmm}-devel
BuildRequires: pam-devel
%description
The Qubes core files for installation inside a Qubes VM.
@ -611,6 +612,7 @@ rm -f %{name}-%{version}
%{python3_sitelib}/dnf-plugins/*
%files qrexec
%config(noreplace) /etc/pam.d/qrexec
/usr/bin/qrexec-fork-server
/usr/bin/qrexec-client-vm
/usr/lib/qubes/qrexec-agent