--- kdebase-3.5.4/kdm/backend/client.c 2006/06/14 18:24:42 551490 +++ kdebase-3.5.4/kdm/backend/client.c 2008/01/31 21:50:25 769223 @@ -51,7 +51,6 @@ #endif #ifdef HAVE_SETUSERCONTEXT # include <login_cap.h> -# define USE_LOGIN_CAP 1 #endif #ifdef USE_PAM # ifdef HAVE_PAM_PAM_APPL_H @@ -66,9 +65,6 @@ extern int loginfailed( const char *User, const char *Host, const char *Tty ); extern int loginsuccess( const char *User, const char *Host, const char *Tty, char **Msg ); #else /* USE_PAM || _AIX */ -# ifdef USESHADOW -# include <shadow.h> -# endif # ifdef KERBEROS # include <sys/param.h> # include <krb.h> @@ -82,6 +78,10 @@ /* for expiration */ # include <time.h> #endif /* USE_PAM || _AIX */ +#ifdef HAVE_SHADOW +# include <shadow.h> +#endif +#include <signal.h> /* * Session data, mostly what struct verify_info was for @@ -101,6 +101,13 @@ char *newdmrc; static struct passwd *p; +#ifdef HAVE_SETUSERCONTEXT +# ifdef HAVE_LOGIN_GETCLASS +login_cap_t *lc; +# else +struct login_cap *lc; +# endif +#endif #ifdef USE_PAM static pam_handle_t *pamh; #elif defined(_AIX) @@ -310,6 +317,10 @@ if (pretc != PAM_SUCCESS) goto pam_bail; } +# ifdef __sun__ /* Only Solaris <= 9, but checking it does not seem worth it. */ + else if (pam_set_item( pamh, PAM_RHOST, 0 ) != PAM_SUCCESS) + goto pam_bail; +# endif # ifdef PAM_FAIL_DELAY pam_set_item( pamh, PAM_FAIL_DELAY, (void *)fail_delay ); # endif @@ -358,6 +369,9 @@ AccNoPass( const char *un ) { struct passwd *pw = 0; +# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */ + struct spwd *spw; +# endif #else AccNoPass( const char *un, struct passwd *pw ) { @@ -381,6 +395,13 @@ #if defined(USE_PAM) || defined(_AIX) if (!(pw = getpwnam( un ))) return 0; + if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') + continue; +# ifdef HAVE_SHADOW /* (sic!) - not USESHADOW */ + if ((spw = getspnam( un )) && + (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*')) + continue; +# endif #endif if (pw->pw_uid) return 1; @@ -410,7 +431,7 @@ return 0; } -#if !defined(USE_PAM) && !defined(_AIX) && defined(USE_LOGIN_CAP) +#if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT) # define LC_RET0 do { login_close(lc); return 0; } while(0) #else # define LC_RET0 return 0 @@ -438,13 +459,6 @@ # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) int tim, expir, warntime, quietlog; # endif -# ifdef USE_LOGIN_CAP -# ifdef HAVE_LOGIN_GETCLASS - login_cap_t *lc; -# else - struct login_cap *lc; -# endif -# endif #endif Debug( "Verify ...\n" ); @@ -583,25 +597,31 @@ if (!(p = getpwnam( curuser ))) { Debug( "getpwnam() failed.\n" ); + gconv( GCONV_PASS, 0 ); V_RET_AUTH; } -# ifdef __linux__ /* only Linux? */ if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { Debug( "account is locked\n" ); + gconv( GCONV_PASS, 0 ); V_RET_AUTH; } -# endif # ifdef USESHADOW - if ((sp = getspnam( curuser ))) + if ((sp = getspnam( curuser ))) { p->pw_passwd = sp->sp_pwdp; - else + if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { + Debug( "account is locked\n" ); + gconv( GCONV_PASS, 0 ); + V_RET_AUTH; + } + } else Debug( "getspnam() failed: %m. Are you root?\n" ); # endif if (!*p->pw_passwd) { if (!td->allowNullPasswd) { Debug( "denying user with empty password\n" ); + gconv( GCONV_PASS, 0 ); V_RET_AUTH; } goto nplogin; @@ -744,23 +764,11 @@ if (msg) free( (void *)msg ); -#else /* USE_PAM || _AIX */ +#endif /* USE_PAM || _AIX */ -# ifdef HAVE_GETUSERSHELL - for (;;) { - if (!(s = getusershell())) { - Debug( "shell not in /etc/shells\n" ); - endusershell(); - V_RET_FAIL( "Your login shell is not listed in /etc/shells" ); - } - if (!strcmp( s, p->pw_shell )) { - endusershell(); - break; - } - } -# endif +#ifndef _AIX -# ifdef USE_LOGIN_CAP +# ifdef HAVE_SETUSERCONTEXT # ifdef HAVE_LOGIN_GETCLASS lc = login_getclass( p->pw_class ); # else @@ -768,106 +776,45 @@ # endif if (!lc) V_RET_FAIL( 0 ); -# endif - -/* restrict_nologin */ -# ifndef _PATH_NOLOGIN -# define _PATH_NOLOGIN "/etc/nologin" + p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell ); # endif - if (( -# ifdef USE_LOGIN_CAP - /* Do we ignore a nologin file? */ - !login_getcapbool( lc, "ignorenologin", 0 )) && - (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) || -# endif - !stat( (nolg = _PATH_NOLOGIN), &st ))) - { - PrepErrorGreet(); - GSendInt( V_MSG_ERR ); - if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) { - if ((buf = Malloc( st.st_size + 1 ))) { - if (read( fd, buf, st.st_size ) == st.st_size) { - buf[st.st_size] = 0; - GSendStr( buf ); - free( buf ); - close( fd ); - GSendInt( V_FAIL ); - LC_RET0; - } - free( buf ); - } - close( fd ); - } - GSendStr( "Logins are not allowed at the moment.\nTry again later" ); - GSendInt( V_FAIL ); - LC_RET0; - } - - -/* restrict_nohome */ -# ifdef USE_LOGIN_CAP - if (login_getcapbool( lc, "requirehome", 0 )) { - struct stat st; - if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) { - PrepErrorGreet(); - GSendInt( V_MSG_ERR ); - GSendStr( "Home folder not available" ); - GSendInt( V_FAIL ); - LC_RET0; - } - } -# endif +# ifndef USE_PAM +/* restrict_expired */ +# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) -/* restrict_time */ -# ifdef USE_LOGIN_CAP -# ifdef HAVE_AUTH_TIMEOK - if (!auth_timeok( lc, time( NULL ) )) { - PrepErrorGreet(); - GSendInt( V_MSG_ERR ); - GSendStr( "You are not allowed to login at the moment" ); - GSendInt( V_FAIL ); - LC_RET0; - } -# endif -# endif - - -/* restrict_expired; this MUST be the last one */ -# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) - -# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(USE_LOGIN_CAP) && defined(USESHADOW)) +# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW)) if (sp) -# endif +# endif { -# define DEFAULT_WARN (2L * 7L) /* Two weeks */ +# define DEFAULT_WARN (2L * 7L) /* Two weeks */ tim = time( NULL ) / 86400L; -# ifdef USE_LOGIN_CAP +# ifdef HAVE_SETUSERCONTEXT quietlog = login_getcapbool( lc, "hushlogin", 0 ); warntime = login_getcaptime( lc, "warnexpire", DEFAULT_WARN * 86400L, DEFAULT_WARN * 86400L ) / 86400L; -# else +# else quietlog = 0; -# ifdef USESHADOW +# ifdef USESHADOW warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN; -# else +# else warntime = DEFAULT_WARN; -# endif -# endif +# endif +# endif -# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE +# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE if (p->pw_expire) { expir = p->pw_expire / 86400L; -# else +# else if (sp->sp_expire != -1) { expir = sp->sp_expire; -# endif +# endif if (tim > expir) { PrepErrorGreet(); GSendInt( V_MSG_ERR ); @@ -888,10 +835,10 @@ } } -# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE +# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE if (p->pw_change) { expir = p->pw_change / 86400L; -# else +# else if (!sp->sp_lstchg) { PrepErrorGreet(); GSendInt( V_MSG_ERR ); @@ -910,7 +857,7 @@ GSendInt( V_FAIL ); LC_RET0; } -# endif +# endif if (tim > expir) { PrepErrorGreet(); GSendInt( V_MSG_ERR ); @@ -934,13 +881,84 @@ } -# endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */ +# endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */ + +/* restrict_nologin */ +# ifndef _PATH_NOLOGIN +# define _PATH_NOLOGIN "/etc/nologin" +# endif + + if (( +# ifdef HAVE_SETUSERCONTEXT + /* Do we ignore a nologin file? */ + !login_getcapbool( lc, "ignorenologin", 0 )) && + (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) || +# endif + !stat( (nolg = _PATH_NOLOGIN), &st ))) + { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) { + if ((buf = Malloc( st.st_size + 1 ))) { + if (read( fd, buf, st.st_size ) == st.st_size) { + buf[st.st_size] = 0; + GSendStr( buf ); + free( buf ); + close( fd ); + GSendInt( V_FAIL ); + LC_RET0; + } + free( buf ); + } + close( fd ); + } + GSendStr( "Logins are not allowed at the moment.\nTry again later" ); + GSendInt( V_FAIL ); + LC_RET0; + } + +/* restrict_time */ +# if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK) + if (!auth_timeok( lc, time( NULL ) )) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "You are not allowed to login at the moment" ); + GSendInt( V_FAIL ); + LC_RET0; + } +# endif + +# ifdef HAVE_GETUSERSHELL + for (;;) { + if (!(s = getusershell())) { + Debug( "shell not in /etc/shells\n" ); + endusershell(); + V_RET_FAIL( "Your login shell is not listed in /etc/shells" ); + } + if (!strcmp( s, p->pw_shell )) { + endusershell(); + break; + } + } +# endif + +# endif /* !USE_PAM */ -# ifdef USE_LOGIN_CAP - login_close( lc ); +/* restrict_nohome */ +# ifdef HAVE_SETUSERCONTEXT + if (login_getcapbool( lc, "requirehome", 0 )) { + struct stat st; + if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) { + PrepErrorGreet(); + GSendInt( V_MSG_ERR ); + GSendStr( "Home folder not available" ); + GSendInt( V_FAIL ); + LC_RET0; + } + } # endif -#endif /* USE_PAM || _AIX */ +#endif /* !_AIX */ return 1; @@ -956,6 +974,54 @@ }; +#if defined(USE_PAM) && defined(HAVE_INITGROUPS) +static int num_saved_gids; +static gid_t *saved_gids; + +static int +saveGids( void ) +{ + num_saved_gids = getgroups( 0, 0 ); + if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids ))) + return 0; + if (getgroups( num_saved_gids, saved_gids ) < 0) { + LogError( "saving groups failed: %m\n" ); + return 0; + } + return 1; +} + +static int +restoreGids( void ) +{ + if (setgroups( num_saved_gids, saved_gids ) < 0) { + LogError( "restoring groups failed: %m\n" ); + return 0; + } + if (setgid( p->pw_gid ) < 0) { + LogError( "restoring gid failed: %m\n" ); + return 0; + } + return 1; +} +#endif /* USE_PAM && HAVE_INITGROUPS */ + +static int +resetGids( void ) +{ +#ifdef HAVE_INITGROUPS + if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) { + LogError( "restoring groups failed: %m\n" ); + return 0; + } +#endif + if (setgid( 0 ) < 0) { + LogError( "restoring gid failed: %m\n" ); + return 0; + } + return 1; +} + static int SetGid( const char *name, int gid ) { @@ -966,6 +1032,7 @@ #ifdef HAVE_INITGROUPS if (initgroups( name, gid ) < 0) { LogError( "initgroups for %s failed: %m\n", name ); + setgid( 0 ); return 0; } #endif /* QNX4 doesn't support multi-groups, no initgroups() */ @@ -985,7 +1052,12 @@ static int SetUser( const char *name, int uid, int gid ) { - return SetGid( name, gid ) && SetUid( name, uid ); + if (SetGid( name, gid )) { + if (SetUid( name, uid )) + return 1; + resetGids(); + } + return 0; } #if defined(SECURE_RPC) || defined(K5AUTH) @@ -1044,6 +1116,10 @@ } static int removeAuth; +#ifdef USE_PAM +static int removeSession; +static int removeCreds; +#endif int StartClient() @@ -1053,6 +1129,9 @@ char **argv, *fname, *str; #ifdef USE_PAM char **pam_env; +# ifdef _AIX + char **saved_env; +# endif struct pam_conv pconv; int pretc; #else @@ -1140,7 +1219,7 @@ env = setEnv( env, "KRBTKFILE", krbtkfile ); #endif userEnviron = inheritEnv( env, envvars ); - env = systemEnv( curuser ); + env = systemEnv( p->pw_name ); systemEnviron = setEnv( env, "HOME", p->pw_dir ); Debug( "user environment:\n%[|''>'\n's" "system environment:\n%[|''>'\n's" @@ -1190,13 +1269,83 @@ mergeSessionArgs( TRUE ); Debug( "now starting the session\n" ); + #ifdef USE_PAM + /* the greeter is gone by now ... */ pconv.conv = PAM_conv_null; pconv.appdata_ptr = 0; - pam_set_item( pamh, PAM_CONV, &pconv ); /* XXX this can fail */ - pam_open_session( pamh, 0 ); /* XXX this can fail, too */ + if ((pretc = pam_set_item( pamh, PAM_CONV, &pconv )) != PAM_SUCCESS) { + ReInitErrorLog(); + LogError( "pam_set_item() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + return 0; + } ReInitErrorLog(); #endif + +#ifdef USE_PAM + +# ifdef HAVE_SETUSERCONTEXT + if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) { + LogError( "setusercontext(groups) for %s failed: %m\n", + curuser ); + return 0; + } +# else + if (!SetGid( curuser, curgid )) + return 0; +# endif + +# ifdef _AIX + if (!(pam_env = initStrArr( 0 ))) { + resetGids(); + return 0; + } + saved_env = environ; + environ = pam_env; +# endif + removeCreds = 1; /* set it first - i don't trust PAM's rollback */ + pretc = pam_setcred( pamh, 0 ); + ReInitErrorLog(); +# ifdef _AIX + pam_env = environ; + environ = saved_env; +# endif +# ifdef HAVE_INITGROUPS + /* This seems to be a strange place for it, but do it: + - after the initial groups are set + - after pam_setcred might have set something, even in the error case + - before pam_setcred(DELETE_CRED) might need it + */ + if (!saveGids()) + return 0; +# endif + if (pretc != PAM_SUCCESS) { + LogError( "pam_setcred() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + resetGids(); + return 0; + } + + removeSession = 1; /* set it first - same as above */ + pretc = pam_open_session( pamh, 0 ); + ReInitErrorLog(); + if (pretc != PAM_SUCCESS) { + LogError( "pam_open_session() for %s failed: %s\n", + curuser, pam_strerror( pamh, pretc ) ); + resetGids(); + return 0; + } + + /* we don't want sessreg and the startup/reset scripts run with user + credentials. unfortunately, we can reset only the gids. */ + resetGids(); + +# define D_LOGIN_SETGROUP LOGIN_SETGROUP +#else /* USE_PAM */ +# define D_LOGIN_SETGROUP 0 +#endif /* USE_PAM */ + removeAuth = 1; chownCtrl( &td->ctrl, curuid ); endpwent(); @@ -1208,6 +1357,7 @@ case 0: sessreg( td, getpid(), curuser, curuid ); + if (source( systemEnviron, td->startup, td_setup )) { LogError( "Cannot execute startup script %\"s\n", td->startup ); exit( 1 ); @@ -1217,32 +1367,16 @@ exit( 1 ); GSet( &mstrtalk ); - /* Do system-dependent login setup here */ setsid(); + Signal( SIGINT, SIG_DFL ); /* Memory leaks are ok here as we exec() soon. */ #if defined(USE_PAM) || !defined(_AIX) -# ifndef HAVE_SETUSERCONTEXT - if (!SetGid( curuser, curgid )) - exit( 1 ); -# endif # ifdef USE_PAM -# ifdef _AIX - if (!(environ = initStrArr( 0 ))) - exit( 1 ); -# endif - if ((pretc = pam_setcred( pamh, 0 )) != PAM_SUCCESS) { - ReInitErrorLog(); - LogError( "pam_setcred() for %s failed: %s\n", - curuser, pam_strerror( pamh, pretc ) ); - exit( 1 ); - } /* pass in environment variables set by libpam and modules it called */ -# ifdef _AIX - pam_env = environ; -# else +# ifndef _AIX pam_env = pam_getenvlist( pamh ); ReInitErrorLog(); # endif @@ -1250,19 +1384,36 @@ for (; *pam_env; pam_env++) userEnviron = putEnv( *pam_env, userEnviron ); # endif -# ifndef HAVE_SETUSERCONTEXT -# ifdef HAVE_SETLOGIN + +# ifdef HAVE_SETLOGIN if (setlogin( curuser ) < 0) { LogError( "setlogin for %s failed: %m\n", curuser ); exit( 1 ); } -# endif +# define D_LOGIN_SETLOGIN LOGIN_SETLOGIN +# else +# define D_LOGIN_SETLOGIN 0 +# endif + +# if defined(USE_PAM) && defined(HAVE_INITGROUPS) + if (!restoreGids()) + exit( 1 ); +# endif + +# ifndef HAVE_SETUSERCONTEXT + +# ifdef USE_PAM if (!SetUid( curuser, curuid )) exit( 1 ); -# else /* HAVE_SETUSERCONTEXT */ +# else + if (!SetUser( curuser, curuid, curgid )) + exit( 1 ); +# endif + +# else /* !HAVE_SETUSERCONTEXT */ /* - * Destroy environment unless user has requested its preservation. + * Destroy environment. * We need to do this before setusercontext() because that may * set or reset some environment variables. */ @@ -1273,7 +1424,9 @@ * Set the user's credentials: uid, gid, groups, * environment variables, resource limits, and umask. */ - if (setusercontext( NULL, p, p->pw_uid, LOGIN_SETALL ) < 0) { + if (setusercontext( lc, p, p->pw_uid, + LOGIN_SETALL & ~(D_LOGIN_SETGROUP|D_LOGIN_SETLOGIN) ) < 0) + { LogError( "setusercontext for %s failed: %m\n", curuser ); exit( 1 ); } @@ -1281,8 +1434,9 @@ for (i = 0; environ[i]; i++) userEnviron = putEnv( environ[i], userEnviron ); -# endif /* HAVE_SETUSERCONTEXT */ -#else /* _AIX */ +# endif /* !HAVE_SETUSERCONTEXT */ + +#else /* PAM || !_AIX */ /* * Set the user's credentials: uid, gid, groups, * audit classes, user limits, and umask. @@ -1466,51 +1620,79 @@ void SessionExit( int status ) { - /* make sure the server gets reset after the session is over */ - if (td->serverPid >= 2) { - if (!td->terminateServer && td->resetSignal) - TerminateProcess( td->serverPid, td->resetSignal ); - } else - ResetServer( td ); - if (removeAuth) { + int pid; #ifdef USE_PAM - int pretc; - if ((pretc = pam_setcred( pamh, PAM_DELETE_CRED )) != PAM_SUCCESS) - LogError( "pam_setcred(DELETE_CRED) for %s failed: %s\n", - curuser, pam_strerror( pamh, pretc ) ); - pam_close_session( pamh, 0 ); - pam_end( pamh, PAM_SUCCESS ); - ReInitErrorLog(); + int pretc; #endif + Signal( SIGTERM, SIG_IGN ); + + if (removeAuth) { if (source( systemEnviron, td->reset, td_setup )) LogError( "Cannot execute reset script %\"s\n", td->reset ); sessreg( td, 0, 0, 0 ); - SetUser( curuser, curuid, curgid ); - RemoveUserAuthorization( td ); + switch ((pid = Fork())) { + case 0: +#if defined(USE_PAM) && defined(HAVE_INITGROUPS) + if (restoreGids() && SetUid( curuser, curuid )) +#else + if (SetUser( curuser, curuid, curgid )) +#endif + + { + RemoveUserAuthorization( td ); #ifdef K5AUTH - Krb5Destroy( td->name ); + Krb5Destroy( td->name ); #endif /* K5AUTH */ #if !defined(USE_PAM) && !defined(_AIX) # ifdef KERBEROS - if (krbtkfile[0]) { - (void)dest_tkt(); + if (krbtkfile[0]) { + (void)dest_tkt(); # ifndef NO_AFS - if (k_hasafs()) - (void)k_unlog(); + if (k_hasafs()) + (void)k_unlog(); # endif - } + } # endif #endif /* !USE_PAM && !_AIX*/ -#ifdef USE_PAM - } else { - if (pamh) { - pam_end( pamh, PAM_SUCCESS ); - ReInitErrorLog(); + } + exit( 0 ); + case -1: + LogError( "Cannot clean up session: fork() failed: %m" ); + break; + default: + Wait4( pid ); + break; } -#endif } + +#ifdef USE_PAM + if (removeCreds) { +# ifdef HAVE_INITGROUPS + restoreGids(); +# endif + if (removeSession) + if ((pretc = pam_close_session( pamh, 0 )) != PAM_SUCCESS) + LogError( "pam_close_session() failed: %s\n", + pam_strerror( pamh, pretc ) ); + if ((pretc = pam_setcred( pamh, PAM_DELETE_CRED )) != PAM_SUCCESS) + LogError( "pam_setcred(DELETE_CRED) failed: %s\n", + pam_strerror( pamh, pretc ) ); + resetGids(); + } + if (pamh) { + pam_end( pamh, PAM_SUCCESS ); + ReInitErrorLog(); + } +#endif + + /* make sure the server gets reset after the session is over */ + if (td->serverPid >= 2) { + if (!td->terminateServer && td->resetSignal) + TerminateProcess( td->serverPid, td->resetSignal ); + } else + ResetServer( td ); Debug( "display %s exiting with status %d\n", td->name, status ); exit( status ); }