shadow: add X11 PAM authentication

This commit is contained in:
Marc-André Moreau 2014-09-26 19:03:48 -04:00
parent 315d16a978
commit 668aa17a22
5 changed files with 216 additions and 0 deletions

View File

@ -61,6 +61,9 @@ typedef int (*pfnShadowSubsystemStop)(rdpShadowSubsystem* subsystem);
typedef int (*pfnShadowEnumMonitors)(MONITOR_DEF* monitors, int maxMonitors);
typedef int (*pfnShadowAuthenticate)(rdpShadowSubsystem* subsystem,
const char* user, const char* domain, const char* password);
typedef int (*pfnShadowSynchronizeEvent)(rdpShadowSubsystem* subsystem, UINT32 flags);
typedef int (*pfnShadowKeyboardEvent)(rdpShadowSubsystem* subsystem, UINT16 flags, UINT16 code);
typedef int (*pfnShadowUnicodeKeyboardEvent)(rdpShadowSubsystem* subsystem, UINT16 flags, UINT16 code);
@ -104,6 +107,7 @@ struct rdp_shadow_server
BOOL mayView;
BOOL mayInteract;
BOOL shareSubRect;
BOOL authentication;
int selectedMonitor;
RECTANGLE_16 subRect;
char* ipcSocket;
@ -148,6 +152,8 @@ struct _RDP_SHADOW_ENTRY_POINTS
pfnShadowMouseEvent MouseEvent; \
pfnShadowExtendedMouseEvent ExtendedMouseEvent; \
\
pfnShadowAuthenticate Authenticate; \
\
rdpShadowServer* server
struct rdp_shadow_subsystem

View File

@ -26,6 +26,22 @@ elseif(APPLE AND NOT IOS)
set(WITH_SHADOW_MAC 1)
endif()
# Authentication
if(WITH_SHADOW_X11 OR WITH_SHADOW_MAC)
set(PAM_FEATURE_TYPE "RECOMMENDED")
set(PAM_FEATURE_PURPOSE "authentication")
set(PAM_FEATURE_DESCRIPTION "user authentication")
find_feature(PAM ${PAM_FEATURE_TYPE} ${PAM_FEATURE_PURPOSE} ${PAM_FEATURE_DESCRIPTION})
if(PAM_FOUND)
add_definitions(-DWITH_PAM)
include_directories(${PAM_INCLUDE_DIR})
list(APPEND ${MODULE_PREFIX}_AUTH_LIBS ${PAM_LIBRARY})
endif()
endif()
if(WITH_SHADOW_X11)
set(XEXT_FEATURE_TYPE "RECOMMENDED")
set(XEXT_FEATURE_PURPOSE "X11 extension")
@ -188,6 +204,8 @@ elseif(WITH_SHADOW_MAC)
list(APPEND ${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_MAC_LIBS})
endif()
list(APPEND ${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_AUTH_LIBS})
add_library(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
list(APPEND ${MODULE_PREFIX}_LIBS freerdp)

View File

@ -31,6 +31,7 @@
#include <X11/Xutil.h>
#include <winpr/crt.h>
#include <winpr/path.h>
#include <winpr/synch.h>
#include <winpr/image.h>
#include <winpr/sysinfo.h>
@ -50,6 +51,156 @@
#define TAG SERVER_TAG("shadow.x11")
#ifdef WITH_PAM
#include <security/pam_appl.h>
struct _SHADOW_PAM_AUTH_DATA
{
const char* user;
const char* domain;
const char* password;
};
typedef struct _SHADOW_PAM_AUTH_DATA SHADOW_PAM_AUTH_DATA;
struct _SHADOW_PAM_AUTH_INFO
{
char* service_name;
pam_handle_t* handle;
struct pam_conv pamc;
SHADOW_PAM_AUTH_DATA appdata;
};
typedef struct _SHADOW_PAM_AUTH_INFO SHADOW_PAM_AUTH_INFO;
int x11_shadow_pam_conv(int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr)
{
int index;
int pam_status = PAM_SUCCESS;
SHADOW_PAM_AUTH_DATA* appdata;
struct pam_response* response;
appdata = (SHADOW_PAM_AUTH_DATA*) appdata_ptr;
response = (struct pam_response*) calloc(num_msg, sizeof(struct pam_response));
if (!response)
return PAM_CONV_ERR;
for (index = 0; index < num_msg; index++)
{
switch (msg[index]->msg_style)
{
case PAM_PROMPT_ECHO_ON:
response[index].resp = _strdup(appdata->user);
response[index].resp_retcode = PAM_SUCCESS;
break;
case PAM_PROMPT_ECHO_OFF:
response[index].resp = _strdup(appdata->password);
response[index].resp_retcode = PAM_SUCCESS;
break;
default:
pam_status = PAM_CONV_ERR;
break;
}
}
if (pam_status != PAM_SUCCESS)
{
free(response);
return pam_status;
}
*resp = response;
return pam_status;
}
int x11_shadow_pam_get_service_name(SHADOW_PAM_AUTH_INFO* info)
{
if (PathFileExistsA("/etc/pam.d/lightdm"))
{
info->service_name = _strdup("lightdm");
}
else if (PathFileExistsA("/etc/pam.d/gdm"))
{
info->service_name = _strdup("gdm");
}
else if (PathFileExistsA("/etc/pam.d/xdm"))
{
info->service_name = _strdup("xdm");
}
else if (PathFileExistsA("/etc/pam.d/login"))
{
info->service_name = _strdup("login");
}
else if (PathFileExistsA("/etc/pam.d/sshd"))
{
info->service_name = _strdup("sshd");
}
else
{
return -1;
}
return 1;
}
int x11_shadow_pam_authenticate(x11ShadowSubsystem* subsystem, const char* user, const char* domain, const char* password)
{
int pam_status;
SHADOW_PAM_AUTH_INFO* info;
info = calloc(1, sizeof(SHADOW_PAM_AUTH_INFO));
if (!info)
return PAM_CONV_ERR;
if (x11_shadow_pam_get_service_name(info) < 0)
return -1;
info->appdata.user = user;
info->appdata.domain = domain;
info->appdata.password = password;
info->pamc.conv = &x11_shadow_pam_conv;
info->pamc.appdata_ptr = &(info->appdata);
pam_status = pam_start(info->service_name, 0, &(info->pamc), &(info->handle));
if (pam_status != PAM_SUCCESS)
{
fprintf(stderr, "pam_start failure: %s\n", pam_strerror(info->handle, pam_status));
free(info);
return -1;
}
pam_status = pam_authenticate(info->handle, 0);
if (pam_status != PAM_SUCCESS)
{
fprintf(stderr, "pam_authenticate failure: %s\n", pam_strerror(info->handle, pam_status));
free(info);
return -1;
}
pam_status = pam_acct_mgmt(info->handle, 0);
if (pam_status != PAM_SUCCESS)
{
fprintf(stderr, "pam_acct_mgmt failure: %s\n", pam_strerror(info->handle, pam_status));
free(info);
return -1;
}
free(info);
return 1;
}
#endif
void x11_shadow_input_synchronize_event(x11ShadowSubsystem* subsystem, UINT32 flags)
{
@ -1092,6 +1243,10 @@ x11ShadowSubsystem* x11_shadow_subsystem_new()
if (!subsystem)
return NULL;
#ifdef WITH_PAM
subsystem->Authenticate = (pfnShadowAuthenticate) x11_shadow_pam_authenticate;
#endif
subsystem->SynchronizeEvent = (pfnShadowSynchronizeEvent) x11_shadow_input_synchronize_event;
subsystem->KeyboardEvent = (pfnShadowKeyboardEvent) x11_shadow_input_keyboard_event;
subsystem->UnicodeKeyboardEvent = (pfnShadowUnicodeKeyboardEvent) x11_shadow_input_unicode_keyboard_event;

View File

@ -132,16 +132,19 @@ BOOL shadow_client_capabilities(freerdp_peer* peer)
BOOL shadow_client_post_connect(freerdp_peer* peer)
{
int authStatus;
int width, height;
rdpSettings* settings;
rdpShadowClient* client;
rdpShadowSurface* lobby;
rdpShadowServer* server;
RECTANGLE_16 invalidRect;
rdpShadowSubsystem* subsystem;
client = (rdpShadowClient*) peer->context;
settings = peer->settings;
server = client->server;
subsystem = server->subsystem;
if (!server->shareSubRect)
{
@ -184,6 +187,29 @@ BOOL shadow_client_post_connect(freerdp_peer* peer)
region16_union_rect(&(lobby->invalidRegion), &(lobby->invalidRegion), &invalidRect);
authStatus = -1;
if (settings->Username && settings->Password)
settings->AutoLogonEnabled = TRUE;
if (settings->AutoLogonEnabled && server->authentication)
{
if (subsystem->Authenticate)
{
authStatus = subsystem->Authenticate(subsystem,
settings->Username, settings->Domain, settings->Password);
}
}
if (server->authentication)
{
if (authStatus < 0)
{
WLog_ERR(TAG, "client authentication failure: %d", authStatus);
return FALSE;
}
}
return TRUE;
}

View File

@ -47,6 +47,7 @@ static COMMAND_LINE_ARGUMENT_A shadow_args[] =
{ "ipc-socket", COMMAND_LINE_VALUE_REQUIRED, "<ipc-socket>", NULL, NULL, -1, NULL, "Server IPC socket" },
{ "monitors", COMMAND_LINE_VALUE_OPTIONAL, "<0,1,2...>", NULL, NULL, -1, NULL, "Select or list monitors" },
{ "rect", COMMAND_LINE_VALUE_REQUIRED, "<x,y,w,h>", NULL, NULL, -1, NULL, "Select rectangle within monitor to share" },
{ "auth", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueFalse, NULL, -1, NULL, "Clients must authenticate" },
{ "may-view", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Clients may view without prompt" },
{ "may-interact", COMMAND_LINE_VALUE_BOOL, NULL, BoolValueTrue, NULL, -1, NULL, "Clients may interact without prompt" },
{ "version", COMMAND_LINE_VALUE_FLAG | COMMAND_LINE_PRINT_VERSION, NULL, NULL, NULL, -1, NULL, "Print version" },
@ -232,6 +233,10 @@ int shadow_server_parse_command_line(rdpShadowServer* server, int argc, char** a
server->subRect.bottom = y + h;
server->shareSubRect = TRUE;
}
CommandLineSwitchCase(arg, "auth")
{
server->authentication = arg->Value ? TRUE : FALSE;
}
CommandLineSwitchDefault(arg)
{
@ -610,6 +615,12 @@ rdpShadowServer* shadow_server_new()
server->mayView = TRUE;
server->mayInteract = TRUE;
#ifdef WITH_SHADOW_X11
server->authentication = TRUE;
#else
server->authentication = FALSE;
#endif
return server;
}