Group :: Graphical desktop/Other
RPM: xlockmore
Main Changelog Spec Patches Sources Download Gear Bugs and FR Repocop
Patch: xlockmore-5.46-pam.patch
Download
Download
xlock/passwd.c | 149 +++++++++++++++++++++++++++++++++++++++++++++++---------
xlock/passwd.h | 23 ++++++++-
xlock/xlock.c | 108 ++++++++++++++++++++++++++++++++++++-----
3 files changed, 244 insertions(+), 36 deletions(-)
diff --git a/xlock/passwd.c b/xlock/passwd.c
index be8ca00..3f130ea 100644
--- a/xlock/passwd.c
+++ b/xlock/passwd.c
@@ -10,6 +10,21 @@ static const char sccsid[] = "@(#)passwd.c 5.00 2000/11/01 xlockmore";
*
* Revision History:
*
+ * Changes made by Paul Wolneykien <manowar@altlinux.ru> (ALT Linux Team)
+ * 14-Oct-08: Fix of the multistage conversation and button logout
+ * conflict:
+ * - adds new 'PAM_conv_count' counter to distinguish first
+ * conversation call from the subsequent calls.
+ * 09-Oct-08: Further PAM integration:
+ * - PAM_conv() function uses interactive conversation to
+ * obtain secret (password) values in general case;
+ * - in the case of a repeated authentication made for the
+ * root user by guess (if allowed) first password entered
+ * during conversation is used;
+ * - return codes of the input/output function
+ * PAM_putText() is analyzed to detect an interruption
+ * signal.
+ *
* Changes maintained by David Bagley <bagleyd@tux.org>
* 23-Feb-2003: Timothy Reed <treed1@twcny.rr.com>
* Updated PAM_conv to call over to xlock to display messages
@@ -278,8 +293,25 @@ char global_user[PASSLENGTH];
#ifdef USE_PAM
-/* used to pass the password to the conversation function */
-static char *PAM_password;
+/* Used to store the first entered password. */
+static char *PAM_password = NULL;
+
+#define FREE_PAM_PASSWORD if (PAM_password != NULL) { \
+ /* Clear plaintext password. */ \
+ bzero(PAM_password, strlen(PAM_password)); \
+ /* Free memory. */ \
+ free(PAM_password); \
+ PAM_password = NULL; \
+}
+
+/* Used to indicate to use the stored password. */
+static int PAM_conv_use_stored = 0;
+
+/* Used to store the result code of the conversation. */
+static int PAM_conv_retcode;
+
+/* Used as counter for the number of conversations. */
+static int PAM_conv_count;
/*-
* PAM conversation function
@@ -298,9 +330,10 @@ PAM_conv(int num_msg,
{
int replies = 0;
struct pam_response *reply = NULL;
+ int isnew = 0;
-#define COPY_STRING(s) (s) ? strdup(s) : NULL
+#define COPY_STRING(s) (s) ? strndup(s, PASSLENGTH) : NULL
reply = (struct pam_response *) malloc(sizeof (struct pam_response) *
num_msg);
@@ -319,6 +352,8 @@ PAM_conv(int num_msg,
reply[replies].resp = NULL;
for (replies = 0; replies < num_msg; replies++) {
+ isnew = PAM_conv_count == 0;
+ PAM_conv_count++;
#ifdef DEBUG
(void) printf( "PAM_conv: message of style (%d) received\n", msg[replies]->msg_style );
(void) printf( " + Message is: (%s)\n", msg[replies]->msg );
@@ -328,17 +363,24 @@ PAM_conv(int num_msg,
#ifdef DEBUG
(void) printf( " + Message style: PAM_PROMPT_ECHO_ON\n" );
#endif
- PAM_putText( msg[replies], &reply[replies], True );
+ PAM_putText(isnew, msg[replies], &reply[replies], True );
/* PAM frees resp */
break;
case PAM_PROMPT_ECHO_OFF:
#ifdef DEBUG
(void) printf( " + Message style: PAM_PROMPT_ECHO_OFF\n" );
#endif
- if( strstr( msg[replies]->msg, "Password" ) == NULL ) {
- PAM_putText( msg[replies], &reply[replies], False );
+ /* If stored password is set use it
+ * for the first reply. */
+ if (replies == 0 && PAM_conv_use_stored && \
+ PAM_password != NULL) {
+ reply[0].resp = COPY_STRING(PAM_password);
+ reply[0].resp_retcode = PAM_SUCCESS;
+#ifdef DEBUG
+ (void) printf( " + Use stored password.\n");
+#endif
} else {
- reply[replies].resp = COPY_STRING(PAM_password);
+ PAM_putText(isnew, msg[replies], &reply[replies], False );
}
/* PAM frees resp */
break;
@@ -346,18 +388,14 @@ PAM_conv(int num_msg,
#ifdef DEBUG
(void) printf( " + Message style: PAM_TEXT_INFO\n" );
#endif
- if( strstr( msg[replies]->msg, "Password" ) == NULL ) {
- PAM_putText( msg[replies], &reply[replies], False );
- }
+ PAM_putText(isnew, msg[replies], &reply[replies], False );
/* PAM frees resp */
break;
case PAM_ERROR_MSG:
#ifdef DEBUG
(void) printf( " + Message style: PAM_ERROR_MSG\n" );
#endif
- if( strstr( msg[replies]->msg, "Password" ) == NULL ) {
- PAM_putText( msg[replies], &reply[replies], False );
- }
+ PAM_putText(isnew, msg[replies], &reply[replies], False );
/* PAM frees resp */
break;
default:
@@ -372,8 +410,45 @@ PAM_conv(int num_msg,
(void) printf( " + Response is: (%s). Return Code is: (%d)\n",
reply[replies].resp, reply[replies].resp_retcode );
#endif
+ /* Non-success return code from the input/output
+ * function is the interruption signal. In that case
+ * authentication should be aborted. */
+ if (reply[replies].resp_retcode != PAM_SUCCESS) {
+ PAM_conv_retcode |= AUTH_INT;
+ /* Don't be optimistic this time: distribute status
+ * of the last reply to the remaining replies, thus
+ * initialize them. */
+ if ((replies + 1) < num_msg) {
+ for (replies += replies; replies < num_msg; replies++) {
+ reply[replies].resp_retcode = reply[replies-1].resp_retcode;
+ reply[replies].resp = NULL;
+ }
+ }
+ return reply[replies].resp_retcode;
+ }
+ /* Empty value is posible sign of the user's attempt
+ * to refuse the authentication. In that case special
+ * return code is also used, but the conversation is not
+ * interrupted. */
+ if (reply[replies].resp == NULL || strlen(reply[replies].resp) == 0) {
+ PAM_conv_retcode |= AUTH_NO_TRY;
+ }
+ /* Saving first password if it wasn't saved yet and
+ * second authentication attempt (for root user) is
+ * possible. */
+ if (allowroot && replies == 0 && PAM_password == NULL) {
+#ifdef DEBUG
+ (void) printf( "PAM_conv: store password.\n");
+#endif
+ PAM_password = COPY_STRING(reply[replies].resp);
+ }
}
*resp = reply;
+#ifdef DEBUG
+ /* Useful for debugging as the conversation can be
+ * interrupted. */
+ (void) printf( "PAM_conv: conversation finished successfully.\n");
+#endif
return PAM_SUCCESS;
}
@@ -1171,7 +1246,8 @@ checkPasswd(char *buffer)
#endif
#define PAM_BAIL if (pam_error != PAM_SUCCESS) { \
- pam_end(pamh, pam_error); BAD_PAM_SETUID return False; \
+ FREE_PAM_PASSWORD \
+ pam_end(pamh, pam_error); BAD_PAM_SETUID return AUTH_NO; \
}
#ifdef BAD_PAM
ruid = getuid(); /* the real user we are running as */
@@ -1180,7 +1256,11 @@ checkPasswd(char *buffer)
#ifdef DEBUG
(void) printf("PAM: Before: UID (%d), EUID (%d)\n", getuid(), geteuid());
#endif
- PAM_password = buffer;
+ /* Reset conversation status and data. */
+ FREE_PAM_PASSWORD
+ PAM_conv_retcode = 0;
+ PAM_conv_use_stored = 0;
+ PAM_conv_count = 0;
pam_error = pam_start(DEFAULT_NAME, user, &PAM_conversation, &pamh);
PAM_BAIL;
@@ -1195,16 +1275,39 @@ checkPasswd(char *buffer)
(void) printf("PAM: pam_authenticate returns code (%d)\n", pam_error);
#endif
if (pam_error != PAM_SUCCESS) {
- if (!allowroot) {
- PAM_BAIL;
- }
+ /* Check if conversation was interrupted and return the
+ * corresponding signal if so. */
+ if (PAM_conv_retcode) {
+#ifdef DEBUG
+ (void) printf("PAM: conversation interrupted.\n");
+#endif
+ FREE_PAM_PASSWORD
+ return PAM_conv_retcode;
+ }
+ /* Try to authenticate second time, as root, using the
+ * stored password if allowed. */
+ if (allowroot) {
+#ifdef DEBUG
+ (void) printf("PAM: try to authenticate as root.\n");
+#endif
+ pam_error = pam_set_item(pamh, PAM_USER, ROOT);
+#ifdef DEBUG
+ (void) printf("PAM: pam_set_item (PAM_USER) returns code (%d)\n", pam_error);
+#endif
+ PAM_BAIL;
- /* Try as root; bail if no success there either */
- pam_error = pam_set_item(pamh, PAM_USER, ROOT);
- PAM_BAIL;
- pam_error = pam_authenticate(pamh, 0);
- PAM_BAIL;
+ /* Set flag to use stored password in place of
+ * conversation with the user. */
+ PAM_conv_use_stored = 1;
+
+ pam_error = pam_authenticate(pamh, 0);
+#ifdef DEBUG
+ (void) printf("PAM: pam_authenticate returns code (%d)\n", pam_error);
+#endif
+ }
+ PAM_BAIL;
}
+ FREE_PAM_PASSWORD
#ifdef BAD_PAM
(void) seteuid(ruid); /* back to user's privileges */
#endif
diff --git a/xlock/passwd.h b/xlock/passwd.h
index 9b27873..5a28eba 100644
--- a/xlock/passwd.h
+++ b/xlock/passwd.h
@@ -9,6 +9,17 @@
* See xlock.c for copying information.
*
* Revision History:
+ *
+ * Changes made by Paul Wolneykien <manowar@altlinux.ru> (ALT Linux Team)
+ * 14-Oct-08: Fix of the multistage conversation and button logout
+ * conflict:
+ * - add 'isnew' parameter to the PAM_putText() function to
+ * manage this value from PAM conversation procedure.
+ * 09-Oct-08: Further PAM integration:
+ * - set of the possible return values of the checkPasswd()
+ * function expanded to represent interruption (icon
+ * pressed) and no-data (just 'enter' pressed) cases.
+ *
* 17-06-99: Started log. :)
*/
@@ -16,8 +27,18 @@ extern void initPasswd(void);
extern int checkPasswd(char *buffer);
#ifdef USE_PAM
+
+/* Not authenticated code .*/
+#define AUTH_NO 0
+/* Successfully authenticated code. */
+#define AUTH_YES 1
+/* Authentication interrupted code. */
+#define AUTH_INT 2
+/* No real authentication attempt. */
+#define AUTH_NO_TRY 4
+
#include <security/pam_appl.h>
/*#include <security/pam_misc.h>*/
-void PAM_putText( const struct pam_message *msg, struct pam_response *resp, Bool PAM_echokeys );
+void PAM_putText(int isnew, const struct pam_message *msg, struct pam_response *resp, Bool PAM_echokeys );
#endif
diff --git a/xlock/xlock.c b/xlock/xlock.c
index 9fa2970..0847699 100644
--- a/xlock/xlock.c
+++ b/xlock/xlock.c
@@ -23,6 +23,26 @@ static const char sccsid[] = "@(#)xlock.c 5.25 2007/06/26 xlockmore";
*
* Revision History:
*
+ * Changes made by Paul Wolneykien <manowar@altlinux.ru> (ALT Linux Team)
+ * 14-Oct-08: Fix of the multistage conversation and button logout
+ * conflict:
+ * - adds wrapper for ReadXString() to keep 'isnew' flag value
+ * for the statusUpdate() function bettween calls;
+ * - add 'isnew' parameter to the PAM_putText() function to
+ * manage this value from PAM conversation procedure.
+ * 09-Oct-08: Further PAM integration:
+ * - in the case of enabled PAM, all authentification data
+ * is acquired through the conversation with a user; no
+ * special conversation for password is made before start
+ * of the PAM conversation;
+ * - PAM conversation input/output appear at the original
+ * password prompt position;
+ * - set of the possible return values of the checkPasswd()
+ * function expanded to represent interruption (icon
+ * pressed) and no-data (just 'enter' pressed) cases;
+ * - PAM_putText() function uses res_retcode field to
+ * signal interruption.
+ *
* Changes maintained by David Bagley <bagleyd@tux.org>
* 23-Feb-03: Timothy Reed <treed1@twcny.rr.com>
* Added PAM_putText to converse with passwd.c PAM_conv function
@@ -1991,7 +2011,7 @@ runMainLoop(int maxtime, int iconscreen)
#endif /* !USE_OLD_EVENT_LOOP */
static int
-ReadXString(char *s, int slen, Bool *capsLock
+ReadXStringX(int isnew, char *s, int slen, Bool *capsLock
#ifdef GLOBAL_UNLOCK
, Bool pass
#elif defined( USE_PAM )
@@ -2020,7 +2040,7 @@ ReadXString(char *s, int slen, Bool *capsLock
call_init_hook((LockStruct *) NULL,
mode_info(dsp, screen, Scr[screen].window, False));
}
- statusUpdate(True, thisscreen);
+ statusUpdate(isnew, thisscreen);
bp = 0;
*s = 0;
@@ -2241,6 +2261,25 @@ ReadXString(char *s, int slen, Bool *capsLock
}
}
+static int
+ReadXString(char *s, int slen, Bool *capsLock
+#ifdef GLOBAL_UNLOCK
+ , Bool pass
+#elif defined( USE_PAM )
+ , Bool PAM_echokeys
+#endif
+
+)
+{
+ return ReadXStringX(True, s, slen, capsLock,
+#ifdef GLOBAL_UNLOCK
+ pass
+#elif defined( USE_PAM )
+ PAM_echokeys
+#endif
+ );
+}
+
void
modeDescription(ModeInfo * mi)
{
@@ -2432,9 +2471,15 @@ getPassword(void)
} else
#endif
{
+#ifdef USE_PAM
+ /* No special password prompt is used in the case of the
+ * PAM-enabled conversation. Current prompt positon is
+ * stored to be used in the conversation input/output
+ * function (PAM_putText()). */
+#else
putText(dsp, Scr[screen].window, Scr[screen].textgc, text_pass, True, left, &x, &y);
putText(dsp, Scr[screen].window, Scr[screen].textgc, " ", False, left, &x, &y);
-
+#endif
passx = x;
passy = y;
}
@@ -2606,14 +2651,26 @@ getPassword(void)
global_user, strlen(global_user));
checkUser(global_user);
#endif
+
+#ifdef USE_PAM
+ /* By this call the whole, possibly multistep,
+ * conversation with the user is done, driven by PAM
+ * subsystem. It finishes either terminated by PAM or
+ * interrupted by the user. */
+ done = checkPasswd(buffer);
+ /* In the case of interruption the control returns to
+ * the screensaver. */
+ if ((done & AUTH_INT) > 0) {
+ break;
+ }
+#else
if (ReadXString(buffer, PASSLENGTH, &capsLock
#ifdef GLOBAL_UNLOCK
, True
-#elif defined( USE_PAM )
- , False
#endif
))
break;
+#endif
y = remy + 6;
#ifdef DIRECTCOLOR_FIX
XSetForeground(dsp, Scr[screen].gc, MI_BLACK_PIXEL(mi));
@@ -2621,6 +2678,11 @@ getPassword(void)
XSetForeground(dsp, Scr[screen].gc, Scr[screen].bg_pixel);
#endif
#ifdef SAFEWORD
+#ifdef USE_PAM
+ /* In the case of PAM all data is acquired and
+ * processed inside the PAM subsystem, so the buffer
+ * may not be set to any value. */
+#else
(void) strcpy(pb->dynpwd, buffer);
pb->mode = EVALUATE_ALL;
pbmain(pb);
@@ -2628,6 +2690,7 @@ getPassword(void)
done = (pb->status == PASS || pb->status == PASS_MASTER);
pb->mode = UPDATE_LOGS;
pbmain(pb);
+#endif
#else
{
XFillRectangle(dsp, Scr[screen].window, Scr[screen].gc,
@@ -2640,11 +2703,22 @@ getPassword(void)
Scr[screen].iconpos.x, y, text_valid, strlen(text_valid));
XFlush(dsp);
+#ifdef USE_PAM
+ /* In the case of using PAM the whole conversation is
+ * made on the previous call to the checkPasswd()
+ * function. */
+
+ /* The conversation can be interrupted by the user by
+ * not entering any data (just hit the 'return' key)
+ * assumed that the user wouldn't be authenticated. */
+ if ((done & AUTH_NO_TRY) > 0) {
+#else
done = checkPasswd(buffer);
if (!done && !*buffer) {
/* just hit return, and it was not his password */
/* count_failed++; */ /* not really an attempt, is it? */
+#endif
break;
} else if (!done) {
/* bad password... log it... */
@@ -3989,15 +4063,18 @@ main(int argc, char **argv)
#ifdef USE_PAM
/* PAM_putText - method to have pam_converse functionality with in XLOCK */
-void PAM_putText( const struct pam_message *msg, struct pam_response *resp, Bool PAM_echokeys )
+void PAM_putText(int isnew, const struct pam_message *msg,
+ struct pam_response *resp, Bool PAM_echokeys )
{
int x = 50, y = 50;
int oldX, oldY;
int left;
char buffer[PASSLENGTH];
+ int rc;
- x = left = Scr[screen].iconpos.x;
- y = Scr[screen].iconpos.y + fontAscent + 200;
+ /* Use current conversation (password) position. */
+ x = left = passx;
+ y = passy;
#ifdef DEBUG
(void) printf( "PAM_putText: message of style %d received: (%s)\n", msg->msg_style, msg->msg );
@@ -4014,7 +4091,7 @@ void PAM_putText( const struct pam_message *msg, struct pam_response *resp, Bool
unamex = x;
unamey = y;
- (void) ReadXString(buffer, PASSLENGTH, (Bool *) NULL
+ rc = ReadXStringX(isnew, buffer, PASSLENGTH, (Bool *) NULL
#ifdef GLOBAL_UNLOCK
, True
#elif defined( USE_PAM )
@@ -4023,11 +4100,18 @@ void PAM_putText( const struct pam_message *msg, struct pam_response *resp, Bool
);
XSetForeground(dsp, Scr[screen].gc, Scr[screen].bg_pixel);
- resp->resp = strdup(buffer);
- resp->resp_retcode = PAM_SUCCESS;
+ if (rc) {
+ /* Signal of interruption. */
+ resp->resp_retcode = PAM_ABORT;
+ resp->resp = NULL;
+ } else {
+ /* Signal of a successfull read. */
+ resp->resp_retcode = PAM_SUCCESS;
+ resp->resp = strdup(buffer);
+ }
#ifdef DEBUG
- (void) printf( "Received Username: (%s)\n", resp->resp );
+ (void) printf( "Received value: (%s)\n", resp->resp );
#endif
}
else