Merge pull request #58 from ArvidNorr/pam-cont

PAM additions mostly for a gateway situation
This commit is contained in:
ArvidNorr 2013-01-31 07:17:42 -08:00
commit 9aa0cb4e61
22 changed files with 263 additions and 47 deletions

View File

@ -41,7 +41,7 @@ typedef int (*ttrans_conn_in)(struct trans* self, struct trans* new_self);
struct trans
{
tbus sck;
tbus sck; /* socket handle */
int mode; /* 1 tcp, 2 unix socket */
int status;
int type1; /* 1 listener 2 server 3 client */

View File

@ -819,6 +819,25 @@ xrdp_mcs_send(struct xrdp_mcs *self, struct stream *s, int chan)
return 0;
}
/**
* Internal help function to close the socket
* @param self
*/
void close_rdp_socket(struct xrdp_mcs *self)
{
if(self->iso_layer->tcp_layer)
{
if(self->iso_layer->tcp_layer->trans)
{
g_tcp_close(self->iso_layer->tcp_layer->trans->sck);
self->iso_layer->tcp_layer->trans->sck = 0 ;
g_writeln("xrdp_mcs_disconnect - socket closed");
return ;
}
}
g_writeln("Failed to close socket");
}
/*****************************************************************************/
/* returns error */
int APP_CC
@ -833,7 +852,8 @@ xrdp_mcs_disconnect(struct xrdp_mcs *self)
if (xrdp_iso_init(self->iso_layer, s) != 0)
{
free_stream(s);
DEBUG((" out xrdp_mcs_disconnect error"));
close_rdp_socket(self);
DEBUG((" out xrdp_mcs_disconnect error - 1"));
return 1;
}
@ -844,11 +864,13 @@ xrdp_mcs_disconnect(struct xrdp_mcs *self)
if (xrdp_iso_send(self->iso_layer, s) != 0)
{
free_stream(s);
DEBUG((" out xrdp_mcs_disconnect error"));
close_rdp_socket(self);
DEBUG((" out xrdp_mcs_disconnect error - 2"));
return 1;
}
free_stream(s);
DEBUG((" out xrdp_mcs_disconnect"));
close_rdp_socket(self);
DEBUG(("xrdp_mcs_disconnect - close sent"));
return 0;
}

View File

@ -42,7 +42,7 @@ access_login_allowed(char *user)
return 0;
}
if (0 == g_cfg->sec.ts_users_enable)
if ((0 == g_cfg->sec.ts_users_enable) && (0==g_cfg->sec.ts_always_group_check))
{
LOG_DBG("Terminal Server Users group is disabled, allowing authentication",
1);
@ -57,7 +57,7 @@ access_login_allowed(char *user)
if (g_cfg->sec.ts_users == gid)
{
LOG_DBG("ts_users is user's primary group");
log_message(LOG_LEVEL_DEBUG,"ts_users is user's primary group");
return 1;
}

View File

@ -36,7 +36,7 @@
*
*/
long DEFAULT_CC
auth_userpass(char* user, char* pass);
auth_userpass(char* user, char* pass, int *errorcode);
/**
*

View File

@ -286,13 +286,18 @@ config_read_security(int file, struct config_security *sc,
sc->ts_admins = gid;
}
}
if (0 == g_strcasecmp(buf, SESMAN_CFG_SEC_ALWAYSGROUPCHECK))
{
sc->ts_always_group_check = text2bool((char *)list_get_item(param_v, i));
}
}
/* printing security config */
g_printf("security configuration:\r\n");
g_printf("\tAllowRootLogin: %i\r\n", sc->allow_root);
g_printf("\tMaxLoginRetry: %i\r\n", sc->login_retry);
g_printf("\tAlwaysGroupCheck: %i\r\n", sc->ts_always_group_check);
if (sc->ts_users_enable)
{
g_printf("\tTSUsersGroup: %i\r\n", sc->ts_users);

View File

@ -56,6 +56,7 @@
#define SESMAN_CFG_SEC_ALLOW_ROOT "AllowRootLogin"
#define SESMAN_CFG_SEC_USR_GROUP "TerminalServerUsers"
#define SESMAN_CFG_SEC_ADM_GROUP "TerminalServerAdmins"
#define SESMAN_CFG_SEC_ALWAYSGROUPCHECK "AlwaysGroupCheck"
#define SESMAN_CFG_SESSIONS "Sessions"
#define SESMAN_CFG_SESS_MAX "MaxSessions"
@ -93,6 +94,11 @@ struct config_security
*/
int ts_admins_enable;
int ts_admins;
/**
* @var ts_always_group_check
* @brief if the Groups are not found deny access
*/
int ts_always_group_check;
};
/**

View File

@ -35,8 +35,9 @@ scp_v0_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s)
int display = 0;
tbus data;
struct session_item *s_item;
int errorcode = 0 ;
data = auth_userpass(s->username, s->password);
data = auth_userpass(s->username, s->password,&errorcode);
if (s->type == SCP_GW_AUTHENTICATION)
{
@ -47,14 +48,14 @@ scp_v0_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s)
if (1 == access_login_allowed(s->username))
{
/* the user is member of the correct groups. */
scp_v0s_replyauthentication(c, 0);
scp_v0s_replyauthentication(c, errorcode);
log_message(LOG_LEVEL_INFO, "Access permitted for user: %s",
s->username);
/* g_writeln("Connection allowed"); */
}
else
{
scp_v0s_replyauthentication(c, 3);
scp_v0s_replyauthentication(c, 32+3); /* all first 32 are reserved for PAM errors */
log_message(LOG_LEVEL_INFO, "Username okey but group problem for "
"user: %s", s->username);
/* g_writeln("user password ok, but group problem"); */
@ -65,7 +66,7 @@ scp_v0_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s)
/* g_writeln("username or password error"); */
log_message(LOG_LEVEL_INFO, "Username or password error for user: %s",
s->username);
scp_v0s_replyauthentication(c, 2);
scp_v0s_replyauthentication(c, errorcode);
}
auth_end(data);

View File

@ -50,7 +50,7 @@ scp_v1_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s)
retries = g_cfg->sec.login_retry;
current_try = retries;
data = auth_userpass(s->username, s->password);
data = auth_userpass(s->username, s->password,NULL);
/*LOG_DBG("user: %s\npass: %s", s->username, s->password);*/
while ((!data) && ((retries == 0) || (current_try > 0)))
@ -65,7 +65,7 @@ scp_v1_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s)
{
case SCP_SERVER_STATE_OK:
/* all ok, we got new username and password */
data = auth_userpass(s->username, s->password);
data = auth_userpass(s->username, s->password,NULL);
/* one try less */
if (current_try > 0)

View File

@ -42,7 +42,7 @@ scp_v1_mng_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s)
int scount;
int end = 0;
data = auth_userpass(s->username, s->password);
data = auth_userpass(s->username, s->password,NULL);
/*LOG_DBG("user: %s\npass: %s", s->username, s->password);*/
if (!data)

View File

@ -10,6 +10,9 @@ AllowRootLogin=1
MaxLoginRetry=4
TerminalServerUsers=tsusers
TerminalServerAdmins=tsadmins
# When AlwaysGroupCheck = false access will be permitted
# if the group TerminalServerUsers is not defined.
AlwaysGroupCheck = false
[Sessions]
X11DisplayOffset=10

View File

@ -48,7 +48,7 @@ auth_account_disabled(struct spwd *stp);
/******************************************************************************/
/* returns boolean */
long DEFAULT_CC
auth_userpass(char *user, char *pass)
auth_userpass(char *user, char *pass, int *errorcode)
{
char salt[13] = "$1$";
char hash[35] = "";

View File

@ -396,7 +396,7 @@ cleanup:
/******************************************************************************/
/* returns boolean */
int DEFAULT_CC
auth_userpass(char *user, char *pass)
auth_userpass(char *user, char *pass, int *errorcode)
{
struct k_opts opts;
struct k5_data k5;

View File

@ -98,9 +98,11 @@ get_service_name(char *service_name)
}
/******************************************************************************/
/* returns long, zero is no go */
/* returns long, zero is no go
Stores the detailed error code in the errorcode variable*/
long DEFAULT_CC
auth_userpass(char *user, char *pass)
auth_userpass(char *user, char *pass, int *errorcode)
{
int error;
struct t_auth_info *auth_info;
@ -116,6 +118,9 @@ auth_userpass(char *user, char *pass)
if (error != PAM_SUCCESS)
{
if(errorcode!=NULL){
*errorcode = error ;
}
g_printf("pam_start failed: %s\r\n", pam_strerror(auth_info->ph, error));
g_free(auth_info);
return 0;
@ -125,16 +130,27 @@ auth_userpass(char *user, char *pass)
if (error != PAM_SUCCESS)
{
if(errorcode!=NULL){
*errorcode = error ;
}
g_printf("pam_authenticate failed: %s\r\n",
pam_strerror(auth_info->ph, error));
g_free(auth_info);
return 0;
}
/* From man page:
The pam_acct_mgmt function is used to determine if the users account is
valid. It checks for authentication token and account expiration and
verifies access restrictions. It is typically called after the user has
been authenticated.
*/
error = pam_acct_mgmt(auth_info->ph, 0);
if (error != PAM_SUCCESS)
{
if(errorcode!=NULL){
*errorcode = error ;
}
g_printf("pam_acct_mgmt failed: %s\r\n",
pam_strerror(auth_info->ph, error));
g_free(auth_info);

View File

@ -34,7 +34,7 @@
/******************************************************************************/
/* returns boolean */
int DEFAULT_CC
auth_userpass(char *user, char *pass)
auth_userpass(char *user, char *pass, int *errorcode)
{
pam_handle_t *pamh;
pam_userpass_t userpass;

View File

@ -177,7 +177,7 @@ void DEFAULT_CC
pipe_sig(int sig_num)
{
/* do nothing */
g_writeln("got SIGPIPE(%d)", sig_num);
g_writeln("got XRDP SIGPIPE(%d)", sig_num);
}
/*****************************************************************************/

View File

@ -25,6 +25,8 @@ tcp_keepalive=yes
#autorun=xrdp1
#hidelogwindow=yes
#bulk_compression=yes
# You can set the PAM error text in a gateway setup (MAX 256 chars)
#pamerrortxt=change your password according to policy at http://url
[Logging]
LogFile=xrdp.log

View File

@ -315,7 +315,18 @@ xrdp_wm_show_edits(struct xrdp_wm *self, struct xrdp_bitmap *combo)
{
self->login_window->focused_control = b;
}
/*Use the domain name as the destination IP/DNS
This is useful in a gateway setup.*/
if (g_strncmp(name, "ip", 255) == 0)
{
/* If the first char in the domain name is '_' we use the domain name as IP*/
if(self->session->client_info->domain[0]=='_')
{
g_strncpy(b->caption1, &self->session->client_info->domain[1], 255);
b->edit_pos = g_mbstowcs(0, b->caption1, 0);
}
}
if (g_strncmp(name, "username", 255) == 0)
{
g_strncpy(b->caption1, self->session->client_info->username, 255);

View File

@ -17,10 +17,12 @@
*
* module manager
*/
#define ACCESS
#include "xrdp.h"
#include "log.h"
#define ACCESS
#ifdef ACCESS
#include "security/_pam_types.h"
#endif
/*****************************************************************************/
struct xrdp_mm *APP_CC
@ -187,9 +189,17 @@ xrdp_mm_send_login(struct xrdp_mm *self)
}
/* send domain */
index = g_strlen(self->wm->client_info->domain);
out_uint16_be(s, index);
out_uint8a(s, self->wm->client_info->domain, index);
if(self->wm->client_info->domain[0]!='_')
{
index = g_strlen(self->wm->client_info->domain);
out_uint16_be(s, index);
out_uint8a(s, self->wm->client_info->domain, index);
}
else
{
out_uint16_be(s, 0);
/* out_uint8a(s, "", 0); */
}
/* send program / shell */
index = g_strlen(self->wm->client_info->program);
@ -1060,12 +1070,12 @@ xrdp_mm_sesman_data_in(struct trans *trans)
int access_control(char *username, char *password, char *srv)
{
int reply;
int rec = 1; // failure
int rec = 32+1; /* 32 is reserved for PAM failures this means connect failure */
struct stream *in_s;
struct stream *out_s;
unsigned long version;
unsigned short int dummy;
unsigned short int ok;
unsigned short int pAM_errorcode;
unsigned short int code;
unsigned long size;
int index;
@ -1117,17 +1127,17 @@ int access_control(char *username, char *password, char *srv)
if ((size == 14) && (version == 0))
{
in_uint16_be(in_s, code);
in_uint16_be(in_s, ok);
in_uint16_be(in_s, pAM_errorcode); /* this variable holds the PAM error code if the variable is >32 it is a "invented" code */
in_uint16_be(in_s, dummy);
if (code != 4)
if (code != 4) /*0x04 means SCP_GW_AUTHENTICATION*/
{
log_message(LOG_LEVEL_ERROR, "Returned cmd code from "
"sesman is corrupt");
}
else
{
rec = ok; /* here we read the reply from the access control */
rec = pAM_errorcode; /* here we read the reply from the access control */
}
}
else
@ -1189,6 +1199,134 @@ void cleanup_states(struct xrdp_mm *self)
self-> usechansrv = 0; /* true if chansrvport is set in xrdp.ini or using sesman */
}
}
#ifdef ACCESS
const char *getPAMError(const int pamError)
{
switch(pamError){
case PAM_SUCCESS:
return "Success";
case PAM_OPEN_ERR:
return "dlopen() failure";
case PAM_SYMBOL_ERR:
return "Symbol not found";
case PAM_SERVICE_ERR:
return "Error in service module";
case PAM_SYSTEM_ERR:
return "System error";
case PAM_BUF_ERR:
return "Memory buffer error";
case PAM_PERM_DENIED:
return "Permission denied";
case PAM_AUTH_ERR:
return "Authentication failure";
case PAM_CRED_INSUFFICIENT:
return "Insufficient credentials to access authentication data";
case PAM_AUTHINFO_UNAVAIL:
return "Authentication service cannot retrieve authentication info.";
case PAM_USER_UNKNOWN:
return "User not known to the underlying authentication module";
case PAM_MAXTRIES:
return "Have exhasted maximum number of retries for service.";
case PAM_NEW_AUTHTOK_REQD:
return "Authentication token is no longer valid; new one required.";
case PAM_ACCT_EXPIRED:
return "User account has expired";
case PAM_CRED_UNAVAIL:
return "Authentication service cannot retrieve user credentials";
case PAM_CRED_EXPIRED:
return "User credentials expired";
case PAM_CRED_ERR:
return "Failure setting user credentials";
case PAM_NO_MODULE_DATA:
return "No module specific data is present";
case PAM_BAD_ITEM:
return "Bad item passed to pam_*_item()";
case PAM_CONV_ERR:
return "Conversation error";
case PAM_AUTHTOK_ERR:
return "Authentication token manipulation error";
case PAM_AUTHTOK_LOCK_BUSY:
return "Authentication token lock busy";
case PAM_AUTHTOK_DISABLE_AGING:
return "Authentication token aging disabled";
case PAM_TRY_AGAIN:
return "Failed preliminary check by password service";
case PAM_IGNORE:
return "Please ignore underlying account module";
case PAM_MODULE_UNKNOWN:
return "Module is unknown";
case PAM_AUTHTOK_EXPIRED:
return "Authentication token expired";
case PAM_CONV_AGAIN:
return "Conversation is waiting for event";
case PAM_INCOMPLETE:
return "Application needs to call libpam again";
case 32+1:
return "Error connecting to PAM";
case 32+3:
return "Username okey but group problem";
default:{
char replytxt[80];
g_sprintf(replytxt,"Not defined PAM error:%d",pamError);
return replytxt ;
}
}
}
const char *getPAMAdditionalErrorInfo(const int pamError,struct xrdp_mm *self)
{
switch(pamError){
case PAM_SUCCESS:
return NULL;
case PAM_OPEN_ERR:
case PAM_SYMBOL_ERR:
case PAM_SERVICE_ERR:
case PAM_SYSTEM_ERR:
case PAM_BUF_ERR:
case PAM_PERM_DENIED:
case PAM_AUTH_ERR:
case PAM_CRED_INSUFFICIENT:
case PAM_AUTHINFO_UNAVAIL:
case PAM_USER_UNKNOWN:
case PAM_CRED_UNAVAIL:
case PAM_CRED_ERR:
case PAM_NO_MODULE_DATA:
case PAM_BAD_ITEM:
case PAM_CONV_ERR:
case PAM_AUTHTOK_ERR:
case PAM_AUTHTOK_LOCK_BUSY:
case PAM_AUTHTOK_DISABLE_AGING:
case PAM_TRY_AGAIN:
case PAM_IGNORE:
case PAM_MODULE_UNKNOWN:
case PAM_CONV_AGAIN:
case PAM_INCOMPLETE:
case _PAM_RETURN_VALUES+1:
case _PAM_RETURN_VALUES+3:
return NULL;
case PAM_MAXTRIES:
case PAM_NEW_AUTHTOK_REQD:
case PAM_ACCT_EXPIRED:
case PAM_CRED_EXPIRED:
case PAM_AUTHTOK_EXPIRED:
if(self->wm->pamerrortxt[0])
{
return self->wm->pamerrortxt;
}
else
{
return "Authentication error - Verify that user/password is valid ";
}
default:{
return "No expected error" ;
}
}
}
#endif
/*****************************************************************************/
int APP_CC
xrdp_mm_connect(struct xrdp_mm *self)
@ -1282,7 +1420,7 @@ xrdp_mm_connect(struct xrdp_mm *self)
{
int reply;
char replytxt[80];
char replymessage[4][80] = {"Ok", "Sesman connect failure", "User or password error", "Privilege group error"};
char *additionalError;
xrdp_wm_log_msg(self->wm, "Please wait, we now perform access control...");
/* g_writeln("we use pam modules to check if we can approve this user"); */
@ -1300,18 +1438,19 @@ xrdp_mm_connect(struct xrdp_mm *self)
/* access_control return 0 on success */
reply = access_control(pam_auth_username, pam_auth_password, pam_auth_sessionIP);
if (reply >= 0 && reply < 4)
{
g_sprintf(replytxt, "Reply from access control: %s", replymessage[reply]);
}
else
{
g_sprintf(replytxt, "Reply from access control undefined");
}
g_sprintf(replytxt, "Reply from access control: %s", getPAMError(reply));
xrdp_wm_log_msg(self->wm, replytxt);
log_message(LOG_LEVEL_INFO, replytxt);
additionalError = getPAMAdditionalErrorInfo(reply,self);
if(additionalError)
{
if(additionalError[0])
{
xrdp_wm_log_msg(self->wm,additionalError);
}
}
if (reply != 0)
{

View File

@ -197,14 +197,14 @@ xrdp_process_main_loop(struct xrdp_process *self)
break;
}
}
/* send disconnect message if possible */
libxrdp_disconnect(self->session);
}
else
{
g_writeln("xrdp_process_main_loop: libxrdp_process_incomming failed");
}
/* Run end in module */
xrdp_process_mod_end(self);
libxrdp_exit(self->session);
self->session = 0;

View File

@ -316,6 +316,7 @@ struct xrdp_wm
int hints;
int allowedchannels[MAX_NR_CHANNELS];
int allowedinitialized ;
char pamerrortxt[256];
};
/* rdp process */

View File

@ -446,6 +446,11 @@ xrdp_wm_load_static_colors_plus(struct xrdp_wm *self, char *autorun_name)
val = (char *)list_get_item(values, index);
self->hide_log_window = text2bool(val);
}
else if (g_strcasecmp(val, "pamerrortxt") == 0)
{
val = (char *)list_get_item(values, index);
g_strncpy(self->pamerrortxt,val,255);
}
}
}
}
@ -534,7 +539,12 @@ xrdp_wm_init(struct xrdp_wm *self)
names->auto_free = 1;
values = list_create();
values->auto_free = 1;
g_strncpy(section_name, self->session->client_info->domain, 255);
/* domain names that starts with '_' are reserved for IP/DNS to simplify
* for the user in a gateway setup */
if(self->session->client_info->domain[0]!='_')
{
g_strncpy(section_name, self->session->client_info->domain, 255);
}
if (section_name[0] == 0)
{

View File

@ -137,7 +137,7 @@ void DEFAULT_CC
pipe_sig(int sig_num)
{
/* do nothing */
g_writeln("got SIGPIPE(%d)", sig_num);
g_writeln("got XRDP WIN SIGPIPE(%d)", sig_num);
}
/*****************************************************************************/