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 (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 * 23-Feb-2003: Timothy Reed * 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 (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 /*#include */ -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 (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 * 23-Feb-03: Timothy Reed * 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