From 668aa17a223b89982870172e6fea858b20099ebf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Marc-Andr=C3=A9=20Moreau?= Date: Fri, 26 Sep 2014 19:03:48 -0400 Subject: [PATCH] shadow: add X11 PAM authentication --- include/freerdp/server/shadow.h | 6 ++ server/shadow/CMakeLists.txt | 18 ++++ server/shadow/X11/x11_shadow.c | 155 ++++++++++++++++++++++++++++++++ server/shadow/shadow_client.c | 26 ++++++ server/shadow/shadow_server.c | 11 +++ 5 files changed, 216 insertions(+) diff --git a/include/freerdp/server/shadow.h b/include/freerdp/server/shadow.h index 46e9e17ed..a3ff1b4b0 100644 --- a/include/freerdp/server/shadow.h +++ b/include/freerdp/server/shadow.h @@ -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 diff --git a/server/shadow/CMakeLists.txt b/server/shadow/CMakeLists.txt index 3ce55436f..4e69817f1 100644 --- a/server/shadow/CMakeLists.txt +++ b/server/shadow/CMakeLists.txt @@ -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) diff --git a/server/shadow/X11/x11_shadow.c b/server/shadow/X11/x11_shadow.c index 0c9ade72c..943bc0b63 100644 --- a/server/shadow/X11/x11_shadow.c +++ b/server/shadow/X11/x11_shadow.c @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -50,6 +51,156 @@ #define TAG SERVER_TAG("shadow.x11") +#ifdef WITH_PAM + +#include + +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; diff --git a/server/shadow/shadow_client.c b/server/shadow/shadow_client.c index 8590d980e..2fe6c1bbe 100644 --- a/server/shadow/shadow_client.c +++ b/server/shadow/shadow_client.c @@ -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; } diff --git a/server/shadow/shadow_server.c b/server/shadow/shadow_server.c index 792102fed..48b0192ed 100644 --- a/server/shadow/shadow_server.c +++ b/server/shadow/shadow_server.c @@ -47,6 +47,7 @@ static COMMAND_LINE_ARGUMENT_A shadow_args[] = { "ipc-socket", COMMAND_LINE_VALUE_REQUIRED, "", 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, "", 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; }