File xrdp-fate318398-change-expired-password.patch of Package xrdp

diff --git a/sesman/auth.h b/sesman/auth.h
index 56f7809..4dd3836 100644
--- a/sesman/auth.h
+++ b/sesman/auth.h
@@ -106,4 +106,6 @@ auth_check_pwd_chg(const char *user);
 int
 auth_change_pwd(const char *user, const char *newpwd);
 
+int
+auth_change_pwd_pam(char* user, char* pass, char* newpwd);
 #endif
diff --git a/sesman/libscp/libscp_session.c b/sesman/libscp/libscp_session.c
index 8df34b3..54d08ae 100644
--- a/sesman/libscp/libscp_session.c
+++ b/sesman/libscp/libscp_session.c
@@ -75,6 +75,10 @@ scp_session_set_type(struct SCP_SESSION *s, tui8 type)
             s->type = SCP_GW_AUTHENTICATION;
             break;
 
+        case SCP_GW_CHAUTHTOK:
+            s->type = SCP_GW_CHAUTHTOK;
+            break;
+
         case SCP_SESSION_TYPE_MANAGE:
             s->type = SCP_SESSION_TYPE_MANAGE;
             s->mng = (struct SCP_MNG_DATA *)g_malloc(sizeof(struct SCP_MNG_DATA), 1);
@@ -236,6 +240,32 @@ scp_session_set_password(struct SCP_SESSION *s, const char *str)
 
 /*******************************************************************/
 int
+scp_session_set_newpass(struct SCP_SESSION *s, char *str)
+{
+    if (0 == str)
+    {
+        log_message(LOG_LEVEL_WARNING, "[session:%d] set_newpass: null newpass", __LINE__);
+        return 1;
+    }
+
+    if (0 != s->newpass)
+    {
+        g_free(s->newpass);
+    }
+
+    s->newpass = g_strdup(str);
+
+    if (0 == s->newpass)
+    {
+        log_message(LOG_LEVEL_WARNING, "[session:%d] set_newpass: strdup error", __LINE__);
+        return 1;
+    }
+
+    return 0;
+}
+
+/*******************************************************************/
+int
 scp_session_set_domain(struct SCP_SESSION *s, const char *str)
 {
     if (0 == str)
diff --git a/sesman/libscp/libscp_types.h b/sesman/libscp/libscp_types.h
index 8cb9166..b4441da 100644
--- a/sesman/libscp/libscp_types.h
+++ b/sesman/libscp/libscp_types.h
@@ -47,6 +47,7 @@
  * XRDP sends this command to let sesman verify if the user is allowed
  * to use the gateway */
 #define SCP_GW_AUTHENTICATION    0x04
+#define SCP_GW_CHAUTHTOK         0x05
 
 #define SCP_ADDRESS_TYPE_IPV4 0x00
 #define SCP_ADDRESS_TYPE_IPV6 0x01
@@ -77,6 +78,7 @@ struct SCP_SESSION
   char  locale[18];
   char* username;
   char* password;
+  char* newpass;
   char* hostname;
   tui8  addr_type;
   tui32 ipv4addr;
diff --git a/sesman/libscp/libscp_v0.c b/sesman/libscp/libscp_v0.c
index 5a0c8bf..4b3fc98 100644
--- a/sesman/libscp/libscp_v0.c
+++ b/sesman/libscp/libscp_v0.c
@@ -317,9 +317,8 @@ scp_v0s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk)
             }
         }
     }
-    else if (code == SCP_GW_AUTHENTICATION)
+    else if (code == SCP_GW_AUTHENTICATION || code == SCP_GW_CHAUTHTOK)
     {
-        /* g_writeln("Command is SCP_GW_AUTHENTICATION"); */
         session = scp_session_create();
 
         if (0 == session)
@@ -329,7 +328,7 @@ scp_v0s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk)
         }
 
         scp_session_set_version(session, version);
-        scp_session_set_type(session, SCP_GW_AUTHENTICATION);
+        scp_session_set_type(session, code);
         /* reading username */
         in_uint16_be(c->in_s, sz);
         buf[sz] = '\0';
@@ -342,6 +341,19 @@ scp_v0s_accept(struct SCP_CONNECTION *c, struct SCP_SESSION **s, int skipVchk)
             /* until syslog merge log_message(s_log, LOG_LEVEL_WARNING, "[v0:%d] connection aborted: error setting        username", __LINE__);*/
             return SCP_SERVER_STATE_INTERNAL_ERR;
         }
+        if (code == SCP_GW_CHAUTHTOK)
+        {
+            /* reading new password */
+            in_uint16_be(c->in_s, sz);
+            buf[sz] = '\0';
+            in_uint8a(c->in_s, buf, sz);
+
+            if (0 != scp_session_set_newpass(session, buf))
+            {
+                scp_session_destroy(session);
+                return SCP_SERVER_STATE_INTERNAL_ERR;
+            }
+        }
 
         /* reading password */
         in_uint16_be(c->in_s, sz);
@@ -417,12 +429,13 @@ scp_v0s_deny_connection(struct SCP_CONNECTION *c)
 
 /******************************************************************************/
 enum SCP_SERVER_STATES_E
-scp_v0s_replyauthentication(struct SCP_CONNECTION *c, unsigned short int value)
+scp_v0s_replyauthentication(struct SCP_CONNECTION *c, unsigned short int value, tui8 type)
 {
     out_uint32_be(c->out_s, 0);  /* version */
     out_uint32_be(c->out_s, 14); /* size */
     /* cmd SCP_GW_AUTHENTICATION means authentication reply */
-    out_uint16_be(c->out_s, SCP_GW_AUTHENTICATION);
+    /* cmd SCP_GW_CHAUTHTOK means chauthtok reply */
+    out_uint16_be(c->out_s, type);
     out_uint16_be(c->out_s, value);  /* reply code  */
     out_uint16_be(c->out_s, 0);  /* dummy data */
     s_mark_end(c->out_s);
diff --git a/sesman/libscp/libscp_v0.h b/sesman/libscp/libscp_v0.h
index 21fc16c..ae54619 100644
--- a/sesman/libscp/libscp_v0.h
+++ b/sesman/libscp/libscp_v0.h
@@ -79,6 +79,6 @@ scp_v0s_deny_connection(struct SCP_CONNECTION* c);
  * @return
  */
 enum SCP_SERVER_STATES_E
-scp_v0s_replyauthentication(struct SCP_CONNECTION* c, unsigned short int value);
+scp_v0s_replyauthentication(struct SCP_CONNECTION* c, unsigned short int value, tui8 type);
 
 #endif
diff --git a/sesman/scp_v0.c b/sesman/scp_v0.c
index de00068..51f1af7 100644
--- a/sesman/scp_v0.c
+++ b/sesman/scp_v0.c
@@ -42,6 +42,13 @@ scp_v0_process(struct SCP_CONNECTION *c, struct SCP_SESSION *s)
     int errorcode = 0;
     bool_t do_auth_end = 1;
 
+    if (s->type == SCP_GW_CHAUTHTOK)
+    {
+        errorcode = auth_change_pwd_pam(s->username, s->password, s->newpass);
+        scp_v0s_replyauthentication(c, errorcode, SCP_GW_CHAUTHTOK);
+        return ;
+    }
+
     data = auth_userpass(s->username, s->password, &errorcode);
 
     if (s->type == SCP_GW_AUTHENTICATION)
@@ -53,14 +60,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, errorcode);
+                scp_v0s_replyauthentication(c, errorcode, SCP_GW_AUTHENTICATION);
                 log_message(LOG_LEVEL_INFO, "Access permitted for user: %s",
                             s->username);
                 /* g_writeln("Connection allowed"); */
             }
             else
             {
-                scp_v0s_replyauthentication(c, 32 + 3); /* all first 32 are reserved for PAM errors */
+                scp_v0s_replyauthentication(c, 32 + 3, SCP_GW_AUTHENTICATION); /* 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"); */
@@ -71,7 +78,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, errorcode);
+            scp_v0s_replyauthentication(c, errorcode, SCP_GW_AUTHENTICATION);
         }
     }
     else if (data)
diff --git a/sesman/verify_user_pam.c b/sesman/verify_user_pam.c
index 15174c2..d996470 100644
--- a/sesman/verify_user_pam.c
+++ b/sesman/verify_user_pam.c
@@ -38,6 +38,7 @@ struct t_user_pass
 {
     char user[256];
     char pass[256];
+    char newpwd[256];
 };
 
 struct t_auth_info
@@ -86,6 +87,55 @@ verify_pam_conv(int num_msg, const struct pam_message **msg,
 }
 
 /******************************************************************************/
+static int
+chauth_pam_conv(int num_msg, const struct pam_message **msg,
+                struct pam_response **resp, void *appdata_ptr)
+{
+    int i;
+    struct pam_response *reply;
+    struct t_user_pass *user_pass;
+
+    reply = g_malloc(sizeof(struct pam_response) * num_msg, 1);
+
+    for (i = 0; i < num_msg; i++)
+    {
+        switch (msg[i]->msg_style)
+        {
+            case PAM_PROMPT_ECHO_ON: /* username */
+                user_pass = appdata_ptr;
+                reply[i].resp = g_strdup(user_pass->user);
+                reply[i].resp_retcode = PAM_SUCCESS;
+                break;
+            case PAM_PROMPT_ECHO_OFF: /* password */
+                user_pass = appdata_ptr;
+                /* only prompt for old password starts with '('
+                   old pass:        "(current) UNIX password:"
+                   new pass:        "New password:"
+                   retype new pass: "Retype new password:" */
+                if (*(msg[i]->msg) == '(')
+                {
+                    reply[i].resp = g_strdup(user_pass->pass);
+                }
+                else
+                {
+                    reply[i].resp = g_strdup(user_pass->newpwd);
+                }
+                reply[i].resp_retcode = PAM_SUCCESS;
+                break;
+            case PAM_TEXT_INFO: /* useless messages */
+                break;
+            default:
+                g_printf("unknown in verify_pam_conv\r\n");
+                g_free(reply);
+                return PAM_CONV_ERR;
+        }
+    }
+
+    *resp = reply;
+    return PAM_SUCCESS;
+}
+
+/******************************************************************************/
 static void
 get_service_name(char *service_name)
 {
@@ -102,6 +152,52 @@ get_service_name(char *service_name)
 }
 
 /******************************************************************************/
+/* returns boolean */
+/* update to the new pass */
+int
+auth_change_pwd_pam(char *user, char *pass, char *newpwd)
+{
+    int error;
+    struct t_auth_info *auth_info;
+    char service_name[256];
+
+    get_service_name(service_name);
+    auth_info = g_malloc(sizeof(struct t_auth_info), 1);
+    g_strncpy(auth_info->user_pass.user, user, 255);
+    g_strncpy(auth_info->user_pass.pass, pass, 255);
+    g_strncpy(auth_info->user_pass.newpwd, newpwd, 255);
+    auth_info->pamc.conv = &chauth_pam_conv;
+    auth_info->pamc.appdata_ptr = &(auth_info->user_pass);
+    error = pam_start(service_name, 0, &(auth_info->pamc), &(auth_info->ph));
+
+    if (error != PAM_SUCCESS)
+    {
+        g_printf("pam_start failed: %s\r\n", pam_strerror(auth_info->ph, error));
+        pam_end(auth_info->ph, error);
+        g_free(auth_info);
+        return error;
+    }
+
+    error = pam_set_item(auth_info->ph, PAM_TTY, service_name);
+    if (error != PAM_SUCCESS)
+    {
+        g_printf("pam_set_item failed: %s\r\n",
+                 pam_strerror(auth_info->ph, error));
+    }
+
+    error = pam_chauthtok(auth_info->ph, PAM_CHANGE_EXPIRED_AUTHTOK);
+    if (error != PAM_SUCCESS)
+    {
+        g_printf("pam_chauthtok failed: %s\r\n",
+                 pam_strerror(auth_info->ph, error));
+        pam_end(auth_info->ph, error);
+        g_free(auth_info);
+        return error;
+    }
+    return error;
+}
+
+/******************************************************************************/
 /* returns long, zero is no go
  Stores the detailed error code in the errorcode variable*/
 
diff --git a/xrdp/xrdp_login_wnd.c b/xrdp/xrdp_login_wnd.c
index 49477a1..160a1ef 100644
--- a/xrdp/xrdp_login_wnd.c
+++ b/xrdp/xrdp_login_wnd.c
@@ -181,7 +181,14 @@ xrdp_wm_cancel_clicked(struct xrdp_bitmap *wnd)
     {
         if (wnd->wm != 0)
         {
-            if (wnd->wm->pro_layer != 0)
+            struct xrdp_bitmap *b1;
+            b1 = xrdp_bitmap_get_child_by_id(wnd, 201);
+            if (b1 != 0 )
+            {
+                /* go back to login window when canceling new password creation */
+                xrdp_wm_set_login_mode(wnd->wm, 0);
+            }
+            else if (wnd->wm->pro_layer != 0)
             {
                 g_set_wait_obj(wnd->wm->pro_layer->self_term_event);
             }
@@ -239,7 +246,29 @@ xrdp_wm_ok_clicked(struct xrdp_bitmap *wnd)
     }
     else
     {
-        log_message(LOG_LEVEL_ERROR, "Combo is 0 - potential programming error");
+        struct xrdp_bitmap *b1;
+        struct xrdp_bitmap *b2;
+        struct xrdp_bitmap *b3;
+        b1 = xrdp_bitmap_get_child_by_id(wnd, 201);
+        b2 = xrdp_bitmap_get_child_by_id(wnd, 203);
+        b3 = xrdp_bitmap_get_child_by_id(wnd, 250);
+        if (b1 != 0 && b2 != 0 && b3 != 0)
+        {
+            if (g_strlen(b1->caption1) > 0 && g_strncmp (b1->caption1, b2->caption1, 255) == 0)
+            {
+                list_add_item (wm->mm->login_names,(tbus)g_strdup("newpass"));
+                list_add_item (wm->mm->login_values,(tbus)g_strdup(b2->caption1));
+                xrdp_wm_set_login_mode (wm, 22);
+            }
+            else
+            {
+                xrdp_wm_set_login_mode(wm, 20);
+            }
+        }
+        else
+        {
+            log_message(LOG_LEVEL_ERROR, "Window not recognized - potential programming error");
+        }
     }
 
     return 0;
@@ -516,6 +545,32 @@ xrdp_wm_login_notify(struct xrdp_bitmap *wnd,
     return 0;
 }
 
+/*****************************************************************************/
+/* change new password window events go here */
+static int
+xrdp_wm_newpass_notify(struct xrdp_bitmap *wnd,
+                       struct xrdp_bitmap *sender,
+                       int msg, long param1, long param2)
+{
+    if (wnd->modal_dialog != 0 && msg != 100)
+    {
+        return 0;
+    }
+
+    if (msg == 1) /* click */
+    {
+        if (sender->id == 2) /* cancel button */
+        {
+            xrdp_wm_cancel_clicked(wnd);
+        }
+        else if (sender->id == 3) /* ok button */
+        {
+            xrdp_wm_ok_clicked(wnd);
+        }
+    }
+    return 0;
+}
+
 /******************************************************************************/
 static int
 xrdp_wm_login_fill_in_combo(struct xrdp_wm *self, struct xrdp_bitmap *b)
@@ -789,6 +844,103 @@ xrdp_login_wnd_create(struct xrdp_wm *self)
     return 0;
 }
 
+/******************************************************************************/
+int
+xrdp_newpass_wnd_create(struct xrdp_wm *self)
+{
+    struct xrdp_bitmap      *but;
+    struct xrdp_cfg_globals *globals;
+    int i;
+
+    globals = &self->xrdp_config->cfg_globals;
+
+    self->newpass_window = xrdp_bitmap_create(globals->ls_width, globals->ls_height, self->screen->bpp,
+                                            WND_TYPE_WND, self);
+    list_add_item(self->screen->child_list, (long)self->newpass_window);
+    self->newpass_window->parent = self->screen;
+    self->newpass_window->owner = self->screen;
+    self->newpass_window->bg_color = globals->ls_bg_color;
+
+    self->newpass_window->left = self->screen->width / 2 -
+                               self->newpass_window->width / 2;
+
+    self->newpass_window->top = self->screen->height / 2 -
+                              self->newpass_window->height / 2;
+
+    self->newpass_window->notify = xrdp_wm_newpass_notify;
+
+    set_string(&self->newpass_window->caption1, "Input new password");
+
+    /* OK button */
+    but = xrdp_bitmap_create(globals->ls_btn_ok_width, globals->ls_btn_ok_height,
+                             self->screen->bpp, WND_TYPE_BUTTON, self);
+    list_add_item(self->newpass_window->child_list, (long)but);
+    but->parent = self->newpass_window;
+    but->owner = self->newpass_window;
+    but->left = globals->ls_btn_ok_x_pos;
+    but->top = globals->ls_btn_ok_y_pos;
+    but->id = 3;
+    set_string(&but->caption1, "OK");
+    but->tab_stop = 1;
+    self->newpass_window->default_button = but;
+
+    /* Cancel button */
+    but = xrdp_bitmap_create(globals->ls_btn_cancel_width,
+                             globals->ls_btn_cancel_height, self->screen->bpp,
+                             WND_TYPE_BUTTON, self);
+    list_add_item(self->newpass_window->child_list, (long)but);
+    but->parent = self->newpass_window;
+    but->owner = self->newpass_window;
+    but->left = globals->ls_btn_cancel_x_pos;
+    but->top = globals->ls_btn_cancel_y_pos;
+    but->id = 2;
+    set_string(&but->caption1, "Cancel");
+    but->tab_stop = 1;
+    self->newpass_window->esc_button = but;
+
+    /* labels and edits */
+    /* id starts between 200 and 249 */
+    char captions [][256] = {"New Pass", "Confirm"};
+    for (i = 0; i < 2; i++)
+    {
+        but = xrdp_bitmap_create(95, DEFAULT_EDIT_H, self->screen->bpp,
+                                 WND_TYPE_LABEL, self);
+        list_add_item(self->newpass_window->child_list, (long)but);
+        but->parent = self->newpass_window;
+        but->owner = self->newpass_window;
+        but->left = globals->ls_label_x_pos;
+        but->top = globals->ls_input_y_pos + (DEFAULT_COMBO_H +5) * i;
+        but->id = 200 + 2 * i;
+        set_string(&but->caption1, captions[i]);
+
+        but = xrdp_bitmap_create(DEFAULT_EDIT_W, DEFAULT_EDIT_H, self->screen->bpp,
+                                 WND_TYPE_EDIT, self);
+        list_add_item(self->newpass_window->child_list, (long)but);
+        but->parent = self->newpass_window;
+        but->owner = self->newpass_window;
+        but->left = globals->ls_input_x_pos;
+        but->top = globals->ls_input_y_pos + (DEFAULT_COMBO_H +5) * i;
+        but->id = 201 + 2 * i;
+        but->pointer = 1;
+        but->tab_stop = 1;
+        but->caption1 = (char *)g_malloc(256, 1);
+        but->password_char = '*';
+    }
+    /* error message label */
+    but = xrdp_bitmap_create (300, DEFAULT_EDIT_H, self->screen->bpp,
+                              WND_TYPE_LABEL, self);
+    list_add_item(self->newpass_window->child_list, (long)but);
+    but->parent = self->newpass_window;
+    but->owner = self->newpass_window;
+    but->left = globals->ls_label_x_pos;
+    but->top = globals->ls_input_y_pos + (DEFAULT_COMBO_H +5) * 2;
+    but->id = 250;
+    but->caption1 = (char *)g_malloc(256, 1);
+    set_string(&but->caption1, "");
+
+    return 0;
+}
+
 /**
  * Load configuration from xrdp.ini file
  *
diff --git a/xrdp/xrdp_mm.c b/xrdp/xrdp_mm.c
index ba4227c..4e15f88 100644
--- a/xrdp/xrdp_mm.c
+++ b/xrdp/xrdp_mm.c
@@ -1458,7 +1458,7 @@ xrdp_mm_sesman_data_in(struct trans *trans)
 /*********************************************************************/
 /* return 0 on success */
 static int
-access_control(char *username, char *password, char *srv)
+access_control(char *username, char *password, char *newpass, char *srv, int type)
 {
     int reply;
     int rec = 32+1; /* 32 is reserved for PAM failures this means connect failure */
@@ -1484,7 +1484,8 @@ access_control(char *username, char *password, char *srv)
             make_stream(out_s);
             init_stream(out_s, 500);
             s_push_layer(out_s, channel_hdr, 8);
-            out_uint16_be(out_s, 4); /*0x04 means SCP_GW_AUTHENTICATION*/
+            out_uint16_be(out_s, type); /*0x04 means SCP_GW_AUTHENTICATION*/
+                                        /*0x05 means SCP_GW_CHAUTHTOK*/
             index = g_strlen(username);
             out_uint16_be(out_s, index);
             out_uint8a(out_s, username, index);
@@ -1492,6 +1493,14 @@ access_control(char *username, char *password, char *srv)
             index = g_strlen(password);
             out_uint16_be(out_s, index);
             out_uint8a(out_s, password, index);
+
+            if (type == 5)
+            {
+                index = g_strlen(newpass);
+                out_uint16_be(out_s, index);
+                out_uint8a(out_s, newpass, index);
+            }
+
             s_mark_end(out_s);
             s_pop_layer(out_s, channel_hdr);
             out_uint32_be(out_s, 0); /* version */
@@ -1521,15 +1530,19 @@ access_control(char *username, char *password, char *srv)
                             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) /*0x04 means SCP_GW_AUTHENTICATION*/
+                            if (code == 4) /*0x04 means SCP_GW_AUTHENTICATION*/
                             {
-                                log_message(LOG_LEVEL_ERROR, "Returned cmd code from "
-                                            "sesman is corrupt");
+                                rec = pAM_errorcode; /* here we read the reply from the access control */
                             }
-                            else
+                            else if (code == 5) /*0x05 means SCP_GW_CHAUTHTOK*/
                             {
                                 rec = pAM_errorcode; /* here we read the reply from the access control */
                             }
+                            else
+                            {
+                                log_message(LOG_LEVEL_ERROR, "Returned cmd code from "
+                                            "sesman is corrupt");
+                            }
                         }
                         else
                         {
@@ -1847,7 +1860,7 @@ xrdp_mm_connect(struct xrdp_mm *self)
     char port[8];
     char chansrvport[256];
 #ifndef USE_NOPAM
-    int use_pam_auth = 0;
+    int use_pam_auth_explicit = 0;
     char pam_auth_sessionIP[256];
     char pam_auth_password[256];
     char pam_auth_username[256];
@@ -1887,7 +1900,7 @@ xrdp_mm_connect(struct xrdp_mm *self)
 #ifndef USE_NOPAM
         else if (g_strcasecmp(name, "pamusername") == 0)
         {
-            use_pam_auth = 1;
+            use_pam_auth_explicit = 1;
             g_strncpy(pam_auth_username, value, 255);
         }
         else if (g_strcasecmp(name, "pamsessionmng") == 0)
@@ -1915,45 +1928,56 @@ xrdp_mm_connect(struct xrdp_mm *self)
     }
 
 #ifndef USE_NOPAM
-    if (use_pam_auth)
-    {
-        int reply;
-        char pam_error[128];
-        const char *additionalError;
-        xrdp_wm_log_msg(self->wm, LOG_LEVEL_DEBUG,
-                        "Please wait, we now perform access control...");
+    int reply;
+    char replytxt[128];
+    char pam_error[128];
+    const char *additionalError;
+    xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "Please wait, we now perform access control...");
 
-        /* g_writeln("we use pam modules to check if we can approve this user"); */
-        if (!g_strncmp(pam_auth_username, "same", 255))
-        {
-            log_message(LOG_LEVEL_DEBUG, "pamusername copied from username - same: %s", username);
-            g_strncpy(pam_auth_username, username, 255);
-        }
+    /* use pam either way, copy from normal user name when not explicitly inputed */
+    if (use_pam_auth_explicit == 0)
+    {
+        log_message(LOG_LEVEL_DEBUG, "pam parameters not defined, copy from user input");
+        g_strncpy(pam_auth_username, username, 255);
+        g_strncpy(pam_auth_password, password, 255);
+        g_strncpy(pam_auth_sessionIP, "127.0.0.1", 255);
+    }
 
-        if (!g_strncmp(pam_auth_password, "same", 255))
-        {
-            log_message(LOG_LEVEL_DEBUG, "pam_auth_password copied from username - same: %s", password);
-            g_strncpy(pam_auth_password, password, 255);
-        }
+    if (!g_strncmp(pam_auth_username, "same", 255))
+    {
+        log_message(LOG_LEVEL_DEBUG, "pamusername copied from username - same: %s", username);
+        g_strncpy(pam_auth_username, username, 255);
+    }
 
-        /* access_control return 0 on success */
-        reply = access_control(pam_auth_username, pam_auth_password, pam_auth_sessionIP);
+    if (!g_strncmp(pam_auth_password, "same", 255))
+    {
+        log_message(LOG_LEVEL_DEBUG, "pam_auth_password copied from password - same: %s", password);
+        g_strncpy(pam_auth_password, password, 255);
+    }
 
-        xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO,
-                        "Reply from access control: %s",
-                        getPAMError(reply, pam_error, 127));
+    /* access_control return 0 on success */
+    reply = access_control(pam_auth_username, pam_auth_password, NULL, pam_auth_sessionIP, 4);
+    xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO,
+                    "Reply from access control: %s",
+                    getPAMError(reply, pam_error, 127));
 
-        additionalError = getPAMAdditionalErrorInfo(reply, self);
-        if (additionalError && additionalError[0])
-        {
-            xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", additionalError);
-        }
+    additionalError = getPAMAdditionalErrorInfo(reply, self);
+    if (additionalError && additionalError[0])
+    {
+        xrdp_wm_log_msg(self->wm, LOG_LEVEL_INFO, "%s", additionalError);
+    }
 
-        if (reply != 0)
+    if (reply != 0)
+    {
+        /* show PAM errors */
+        xrdp_wm_show_log(self->wm);
+        if (reply == PAM_NEW_AUTHTOK_REQD)
         {
-            rv = 1;
-            return rv;
+            /* show new password window */
+            xrdp_wm_set_login_mode(self->wm, 20);
         }
+        rv = 1;
+        return rv;
     }
 #endif
 
@@ -2047,6 +2071,59 @@ xrdp_mm_connect(struct xrdp_mm *self)
 }
 
 /*****************************************************************************/
+/* return 0 on success */
+int
+xrdp_mm_change_expired_password(struct xrdp_mm *self)
+{
+    int rv = -1;
+    int index;
+    int count;
+    int old_idx;
+    int new_idx;
+    char *username;
+    char *password;
+    char *newpass;
+    char sessionIP[256];
+    char *name;
+    char *value;
+
+    username = 0;
+    password = 0;
+    count = self->login_names->count;
+
+    for (index = 0; index < count; index++)
+    {
+        name = (char *)list_get_item(self->login_names, index);
+        value = (char *)list_get_item(self->login_values, index);
+
+        if (g_strcasecmp(name, "username") == 0)
+        {
+            username = value;
+        }
+        else if (g_strcasecmp(name, "password") == 0)
+        {
+            password = value;
+            old_idx = index;
+        }
+        else if (g_strcasecmp(name, "newpass") == 0)
+        {
+            newpass = value;
+            new_idx = index;
+        }
+        g_strncpy(sessionIP, "127.0.0.1", 255);
+    }
+    rv = access_control(username, password, newpass, sessionIP, 5);
+    if (rv == 0)
+    {
+        list_remove_item (self->login_names, old_idx);
+        list_remove_item (self->login_values, old_idx);
+        list_add_item (self->login_names, (tbus)g_strdup("password"));
+        list_add_item (self->login_values, (tbus)g_strdup(newpass));
+    }
+    return rv;
+}
+
+/*****************************************************************************/
 int
 xrdp_mm_get_wait_objs(struct xrdp_mm *self,
                       tbus *read_objs, int *rcount,
diff --git a/xrdp/xrdp_types.h b/xrdp/xrdp_types.h
index 75c70ee..344e7fc 100644
--- a/xrdp/xrdp_types.h
+++ b/xrdp/xrdp_types.h
@@ -325,6 +325,7 @@ struct xrdp_wm
   struct xrdp_cache* cache;
   int palette[256];
   struct xrdp_bitmap* login_window;
+  struct xrdp_bitmap* newpass_window;
   /* generic colors */
   int black;
   int grey;
diff --git a/xrdp/xrdp_wm.c b/xrdp/xrdp_wm.c
index 8a6695f..238c5cf 100644
--- a/xrdp/xrdp_wm.c
+++ b/xrdp/xrdp_wm.c
@@ -1855,6 +1855,34 @@ xrdp_wm_login_mode_changed(struct xrdp_wm *self)
         self->dragging = 0;
         xrdp_wm_set_login_mode(self, 11);
     }
+    else if (self->login_mode == 20)
+    {
+        /* keep log window open */
+        if (self->log_wnd == 0)
+        {
+            xrdp_wm_delete_all_children(self);
+        }
+        /* show update expired password window */
+        self->dragging = 0;
+        xrdp_newpass_wnd_create(self);
+        xrdp_bitmap_invalidate(self->screen, 0);
+        xrdp_wm_set_focused(self, self->newpass_window);
+        xrdp_wm_set_login_mode(self, 21);
+    }
+    else if (self->login_mode == 22)
+    {
+        /* do change expired password session */
+        xrdp_wm_delete_all_children(self);
+        self->dragging = 0;
+        if (xrdp_mm_change_expired_password(self->mm) == 0)
+        {
+            xrdp_wm_set_login_mode(self, 2); /* with password updated, connect again */
+        }
+        else
+        {
+            xrdp_wm_set_login_mode(self, 20); /* try to change password again */
+        }
+    }
 
     return 0;
 }
@@ -1899,11 +1927,19 @@ xrdp_wm_log_wnd_notify(struct xrdp_bitmap *wnd,
             xrdp_bitmap_invalidate(wm->screen, &rect);
 
             /* if module is gone, reset the session when ok is clicked */
+            /* unless we are to update password */
             if (wm->mm->mod_handle == 0)
             {
                 /* make sure autologin is off */
                 wm->session->client_info->rdp_autologin = 0;
-                xrdp_wm_set_login_mode(wm, 0); /* reset session */
+                if (wm->login_mode == 21)
+                {
+                    xrdp_wm_set_login_mode(wm, 20); /* try update password again */
+                }
+                else
+                {
+                    xrdp_wm_set_login_mode(wm, 0); /* reset session */
+                }
             }
         }
     }
@@ -1965,6 +2001,9 @@ xrdp_wm_show_log(struct xrdp_wm *self)
         return 0;
     }
 
+    /* delete all dialogs, they will be created when needed anyway */
+    xrdp_wm_delete_all_children(self);
+
     if (self->log_wnd == 0)
     {
         w = DEFAULT_WND_LOG_W;
openSUSE Build Service is sponsored by