summaryrefslogtreecommitdiffstats
path: root/kdm/backend/client.c
diff options
context:
space:
mode:
Diffstat (limited to 'kdm/backend/client.c')
-rw-r--r--kdm/backend/client.c1760
1 files changed, 1760 insertions, 0 deletions
diff --git a/kdm/backend/client.c b/kdm/backend/client.c
new file mode 100644
index 000000000..c807b6e15
--- /dev/null
+++ b/kdm/backend/client.c
@@ -0,0 +1,1760 @@
+/*
+
+Copyright 1988, 1998 The Open Group
+Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org>
+
+Permission to use, copy, modify, distribute, and sell this software and its
+documentation for any purpose is hereby granted without fee, provided that
+the above copyright notice appear in all copies and that both that
+copyright notice and this permission notice appear in supporting
+documentation.
+
+The above copyright notice and this permission notice shall be included
+in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
+OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
+OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
+ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
+OTHER DEALINGS IN THE SOFTWARE.
+
+Except as contained in this notice, the name of a copyright holder shall
+not be used in advertising or otherwise to promote the sale, use or
+other dealings in this Software without prior written authorization
+from the copyright holder.
+
+*/
+
+/*
+ * xdm - display manager daemon
+ * Author: Keith Packard, MIT X Consortium
+ *
+ * user verification and session initiation.
+ */
+
+#include "dm.h"
+#include "dm_auth.h"
+#include "dm_error.h"
+
+#include <sys/stat.h>
+#include <pwd.h>
+#include <grp.h>
+#ifdef SECURE_RPC
+# include <rpc/rpc.h>
+# include <rpc/key_prot.h>
+extern int key_setnet( struct key_netstarg *arg );
+#endif
+#ifdef K5AUTH
+# include <krb5/krb5.h>
+#endif
+#ifdef HAVE_SETUSERCONTEXT
+# include <login_cap.h>
+#endif
+#ifdef USE_PAM
+# ifdef HAVE_PAM_PAM_APPL_H
+# include <pam/pam_appl.h>
+# else
+# include <security/pam_appl.h>
+# endif
+#elif defined(_AIX) /* USE_PAM */
+# include <login.h>
+# include <usersec.h>
+extern int loginrestrictions( const char *Name, const int Mode, const char *Tty, char **Msg );
+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 KERBEROS
+# include <sys/param.h>
+# include <krb.h>
+# ifndef NO_AFS
+# include <kafs.h>
+# endif
+# endif
+/* for nologin */
+# include <sys/types.h>
+# include <unistd.h>
+/* 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
+ */
+char *curuser;
+char *curpass;
+char *curtype;
+char *newpass;
+char **userEnviron;
+char **systemEnviron;
+static int curuid;
+static int curgid;
+int cursource;
+
+char *dmrcuser;
+char *curdmrc;
+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)
+static char tty[16], hostname[100];
+#else
+# ifdef USESHADOW
+static struct spwd *sp;
+# endif
+# ifdef KERBEROS
+static char krbtkfile[MAXPATHLEN];
+# endif
+#endif
+
+#define V_RET_AUTH \
+ do { \
+ PrepErrorGreet (); \
+ GSendInt (V_AUTH); \
+ return 0; \
+ } while(0)
+
+#define V_RET_FAIL(m) \
+ do { \
+ PrepErrorGreet (); \
+ GSendInt (V_MSG_ERR); \
+ GSendStr (m); \
+ GSendInt (V_FAIL); \
+ return 0; \
+ } while(0)
+
+#ifdef USE_PAM
+
+# ifdef PAM_MESSAGE_NONCONST
+typedef struct pam_message pam_message_type;
+typedef void *pam_gi_type;
+# else
+typedef const struct pam_message pam_message_type;
+typedef const void *pam_gi_type;
+# endif
+
+struct pam_data {
+ GConvFunc gconv;
+ int usecur;
+ int abort;
+};
+
+static int
+PAM_conv( int num_msg,
+ pam_message_type **msg,
+ struct pam_response **resp,
+ void *appdata_ptr )
+{
+ int count;
+ struct pam_response *reply;
+ struct pam_data *pd = (struct pam_data *)appdata_ptr;
+
+ if (!(reply = Calloc( num_msg, sizeof(*reply) )))
+ return PAM_CONV_ERR;
+
+ ReInitErrorLog();
+ Debug( "PAM_conv\n" );
+ for (count = 0; count < num_msg; count++)
+ switch (msg[count]->msg_style) {
+ case PAM_TEXT_INFO:
+ Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg );
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( msg[count]->msg );
+ continue;
+ case PAM_ERROR_MSG:
+ Debug( " PAM_ERROR_MSG: %s\n", msg[count]->msg );
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( msg[count]->msg );
+ continue;
+ default:
+ /* could do better error handling here, but see below ... */
+ if (pd->usecur) {
+ switch (msg[count]->msg_style) {
+ /* case PAM_PROMPT_ECHO_ON: cannot happen */
+ case PAM_PROMPT_ECHO_OFF:
+ Debug( " PAM_PROMPT_ECHO_OFF (usecur): %s\n", msg[count]->msg );
+ if (!curpass)
+ pd->gconv( GCONV_PASS, 0 );
+ StrDup( &reply[count].resp, curpass );
+ break;
+ default:
+ LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
+ goto conv_err;
+ }
+ } else {
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ Debug( " PAM_PROMPT_ECHO_ON: %s\n", msg[count]->msg );
+ reply[count].resp = pd->gconv( GCONV_NORMAL, msg[count]->msg );
+ break;
+ case PAM_PROMPT_ECHO_OFF:
+ Debug( " PAM_PROMPT_ECHO_OFF: %s\n", msg[count]->msg );
+ reply[count].resp = pd->gconv( GCONV_HIDDEN, msg[count]->msg );
+ break;
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT:
+ Debug( " PAM_BINARY_PROMPT\n" );
+ reply[count].resp = pd->gconv( GCONV_BINARY, msg[count]->msg );
+ break;
+#endif
+ default:
+ LogError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
+ goto conv_err;
+ }
+ }
+ if (!reply[count].resp) {
+ Debug( " PAM_conv aborted\n" );
+ pd->abort = TRUE;
+ goto conv_err;
+ }
+ reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
+ }
+ Debug( " PAM_conv success\n" );
+ *resp = reply;
+ return PAM_SUCCESS;
+
+ conv_err:
+ for (; count >= 0; count--)
+ if (reply[count].resp)
+ switch (msg[count]->msg_style) {
+ case PAM_PROMPT_ECHO_ON:
+ case PAM_PROMPT_ECHO_OFF: /* could wipe ... */
+#ifdef PAM_BINARY_PROMPT
+ case PAM_BINARY_PROMPT: /* ... that too ... */
+#endif
+ free( reply[count].resp );
+ break;
+ }
+ free( reply );
+ return PAM_CONV_ERR;
+}
+
+static int
+PAM_conv_null( int num_msg,
+ pam_message_type **msg,
+ struct pam_response **resp,
+ void *appdata_ptr ATTR_UNUSED )
+{
+ int count;
+ struct pam_response *reply;
+
+ if (!(reply = Calloc( num_msg, sizeof(*reply) )))
+ return PAM_CONV_ERR;
+
+ ReInitErrorLog();
+ Debug( "PAM_conv_null\n" );
+ for (count = 0; count < num_msg; count++) {
+ switch (msg[count]->msg_style) {
+ case PAM_TEXT_INFO:
+ Debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg );
+ continue;
+ case PAM_ERROR_MSG:
+ LogError( "PAM error message: %s\n", msg[count]->msg );
+ continue;
+ default:
+ /* unknown */
+ Debug( " PAM_<%d>\n", msg[count]->msg_style );
+ free( reply );
+ return PAM_CONV_ERR;
+ }
+ reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
+ }
+ Debug( " PAM_conv_null success\n" );
+ *resp = reply;
+ return PAM_SUCCESS;
+}
+
+# ifdef PAM_FAIL_DELAY
+static void
+fail_delay( int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED,
+ void *appdata_ptr ATTR_UNUSED )
+{}
+# endif
+
+static int
+doPAMAuth( const char *psrv, struct pam_data *pdata )
+{
+ pam_gi_type pitem;
+ struct pam_conv pconv;
+ int pretc;
+
+ pdata->abort = FALSE;
+ pconv.conv = PAM_conv;
+ pconv.appdata_ptr = (void *)pdata;
+ Debug( " PAM service %s\n", psrv );
+ if ((pretc = pam_start( psrv, curuser, &pconv, &pamh )) != PAM_SUCCESS)
+ goto pam_bail2;
+ if ((pretc = pam_set_item( pamh, PAM_TTY, td->name )) != PAM_SUCCESS) {
+ pam_bail:
+ pam_end( pamh, pretc );
+ pamh = 0;
+ pam_bail2:
+ ReInitErrorLog();
+ LogError( "PAM error: %s\n", pam_strerror( 0, pretc ) );
+ V_RET_FAIL( 0 );
+ }
+ if ((td->displayType & d_location) == dForeign) {
+ char *cp = strchr( td->name, ':' );
+ *cp = 0;
+ pretc = pam_set_item( pamh, PAM_RHOST, td->name );
+ *cp = ':';
+ 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
+ ReInitErrorLog();
+
+ Debug( " pam_authenticate() ...\n" );
+ pretc = pam_authenticate( pamh,
+ td->allowNullPasswd ? 0 : PAM_DISALLOW_NULL_AUTHTOK );
+ ReInitErrorLog();
+ Debug( " pam_authenticate() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pdata->abort) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ return 0;
+ }
+ if (!curuser) {
+ Debug( " asking PAM for user ...\n" );
+ pam_get_item( pamh, PAM_USER, &pitem );
+ ReInitErrorLog();
+ StrDup( &curuser, (const char *)pitem );
+ GSendInt( V_PUT_USER );
+ GSendStr( curuser );
+ }
+ if (pretc != PAM_SUCCESS) {
+ switch (pretc) {
+ case PAM_USER_UNKNOWN:
+ case PAM_AUTH_ERR:
+ case PAM_MAXTRIES: /* should handle this better ... */
+ case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_AUTH;
+ default:
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_FAIL( 0 );
+ }
+ }
+ return 1;
+}
+
+#endif /* USE_PAM */
+
+static int
+#if defined(USE_PAM) || defined(_AIX)
+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 )
+{
+#endif
+ struct group *gr;
+ char **fp;
+ int hg;
+
+ if (!*un)
+ return 0;
+
+ if (cursource != PWSRC_MANUAL)
+ return 1;
+
+ for (hg = 0, fp = td->noPassUsers; *fp; fp++)
+ if (**fp == '@')
+ hg = 1;
+ else if (!strcmp( un, *fp ))
+ return 1;
+ else if (!strcmp( "*", *fp )) {
+#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;
+ }
+
+#if defined(USE_PAM) || defined(_AIX)
+ if (hg && (pw || (pw = getpwnam( un )))) {
+#else
+ if (hg) {
+#endif
+ for (setgrent(); (gr = getgrent()); )
+ for (fp = td->noPassUsers; *fp; fp++)
+ if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) {
+ if (pw->pw_gid == gr->gr_gid) {
+ endgrent();
+ return 1;
+ }
+ for (; *gr->gr_mem; gr->gr_mem++)
+ if (!strcmp( un, *gr->gr_mem )) {
+ endgrent();
+ return 1;
+ }
+ }
+ endgrent();
+ }
+
+ return 0;
+}
+
+#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
+#endif
+
+int
+Verify( GConvFunc gconv, int rootok )
+{
+#ifdef USE_PAM
+ const char *psrv;
+ struct pam_data pdata;
+ int pretc, pnopass;
+ char psrvb[64];
+#elif defined(_AIX)
+ char *msg, *curret;
+ int i, reenter;
+#else
+ struct stat st;
+ const char *nolg;
+ char *buf;
+ int fd;
+# ifdef HAVE_GETUSERSHELL
+ char *s;
+# endif
+# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
+ int tim, expir, warntime, quietlog;
+# endif
+#endif
+
+ Debug( "Verify ...\n" );
+
+#ifdef USE_PAM
+
+ pnopass = FALSE;
+ if (!strcmp( curtype, "classic" )) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ if (AccNoPass( curuser )) {
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ pnopass = TRUE;
+ sprintf( psrvb, "%.31s-np", PAMService );
+ psrv = psrvb;
+ } else
+ psrv = PAMService;
+ } else
+ psrv = PAMService;
+ pdata.usecur = TRUE;
+ } else {
+ sprintf( psrvb, "%.31s-%.31s", PAMService, curtype );
+ psrv = psrvb;
+ pdata.usecur = FALSE;
+ }
+ pdata.gconv = gconv;
+ if (!doPAMAuth( psrv, &pdata ))
+ return 0;
+
+#elif defined(_AIX)
+
+ if ((td->displayType & d_location) == dForeign) {
+ char *tmpch;
+ strncpy( hostname, td->name, sizeof(hostname) - 1 );
+ hostname[sizeof(hostname)-1] = '\0';
+ if ((tmpch = strchr( hostname, ':' )))
+ *tmpch = '\0';
+ } else
+ hostname[0] = '\0';
+
+ /* tty names should only be 15 characters long */
+# if 0
+ for (i = 0; i < 15 && td->name[i]; i++) {
+ if (td->name[i] == ':' || td->name[i] == '.')
+ tty[i] = '_';
+ else
+ tty[i] = td->name[i];
+ }
+ tty[i] = '\0';
+# else
+ memcpy( tty, "/dev/xdm/", 9 );
+ for (i = 0; i < 6 && td->name[i]; i++) {
+ if (td->name[i] == ':' || td->name[i] == '.')
+ tty[9 + i] = '_';
+ else
+ tty[9 + i] = td->name[i];
+ }
+ tty[9 + i] = '\0';
+# endif
+
+ if (!strcmp( curtype, "classic" )) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ if (AccNoPass( curuser )) {
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ Debug( "accepting despite empty password\n" );
+ goto done;
+ }
+ } else
+ if (!gconv( GCONV_PASS, 0 ))
+ return 0;
+ enduserdb();
+ msg = NULL;
+ if ((i = authenticate( curuser, curpass, &reenter, &msg ))) {
+ Debug( "authenticate() failed: %s\n", msg );
+ if (msg)
+ free( msg );
+ loginfailed( curuser, hostname, tty );
+ if (i == ENOENT || i == ESAD)
+ V_RET_AUTH;
+ else
+ V_RET_FAIL( 0 );
+ }
+ if (reenter) {
+ LogError( "authenticate() requests more data: %s\n", msg );
+ free( msg );
+ V_RET_FAIL( 0 );
+ }
+ } else if (!strcmp( curtype, "generic" )) {
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+ for (curret = 0;;) {
+ msg = NULL;
+ if ((i = authenticate( curuser, curret, &reenter, &msg ))) {
+ Debug( "authenticate() failed: %s\n", msg );
+ if (msg)
+ free( msg );
+ loginfailed( curuser, hostname, tty );
+ if (i == ENOENT || i == ESAD)
+ V_RET_AUTH;
+ else
+ V_RET_FAIL( 0 );
+ }
+ if (curret)
+ free( curret );
+ if (!reenter)
+ break;
+ if (!(curret = gconv( GCONV_HIDDEN, msg )))
+ return 0;
+ free( msg );
+ }
+ } else {
+ LogError( "Unsupported authentication type %\"s requested\n", curtype );
+ V_RET_FAIL( 0 );
+ }
+ if (msg) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( msg );
+ free( msg );
+ }
+
+ done:
+
+#else
+
+ if (strcmp( curtype, "classic" )) {
+ LogError( "Unsupported authentication type %\"s requested\n", curtype );
+ V_RET_FAIL( 0 );
+ }
+
+ if (!gconv( GCONV_USER, 0 ))
+ return 0;
+
+ if (!(p = getpwnam( curuser ))) {
+ Debug( "getpwnam() failed.\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+ if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
+ Debug( "account is locked\n" );
+ gconv( GCONV_PASS, 0 );
+ V_RET_AUTH;
+ }
+
+# ifdef USESHADOW
+ if ((sp = getspnam( curuser ))) {
+ p->pw_passwd = sp->sp_pwdp;
+ 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;
+ }
+
+ if (AccNoPass( curuser, p )) {
+ nplogin:
+ gconv( GCONV_PASS_ND, 0 );
+ if (!*curpass) {
+ Debug( "accepting password-less login\n" );
+ goto done;
+ }
+ } else
+ if (!gconv( GCONV_PASS, 0 ))
+ return 0;
+
+# ifdef KERBEROS
+ if (p->pw_uid) {
+ int ret;
+ char realm[REALM_SZ];
+
+ if (krb_get_lrealm( realm, 1 )) {
+ LogError( "Can't get KerberosIV realm.\n" );
+ V_RET_FAIL( 0 );
+ }
+
+ sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name );
+ krb_set_tkt_string( krbtkfile );
+ unlink( krbtkfile );
+
+ ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" );
+ if (ret == KSUCCESS) {
+ chown( krbtkfile, p->pw_uid, p->pw_gid );
+ Debug( "KerberosIV verify succeeded\n" );
+ goto done;
+ } else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) {
+ LogError( "KerberosIV verification failure %\"s for %s\n",
+ krb_get_err_text( ret ), curuser );
+ krbtkfile[0] = '\0';
+ V_RET_FAIL( 0 );
+ }
+ Debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) );
+ }
+ krbtkfile[0] = '\0';
+# endif /* KERBEROS */
+
+# if defined(ultrix) || defined(__ultrix__)
+ if (authenticate_user( p, curpass, NULL ) < 0)
+# elif defined(HAVE_CRYPT)
+ if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd ))
+# else
+ if (strcmp( curpass, p->pw_passwd ))
+# endif
+ {
+ Debug( "password verify failed\n" );
+ V_RET_AUTH;
+ }
+
+ done:
+
+#endif /* !defined(USE_PAM) && !defined(_AIX) */
+
+ Debug( "restrict %s ...\n", curuser );
+
+#if defined(USE_PAM) || defined(_AIX)
+ if (!(p = getpwnam( curuser ))) {
+ LogError( "getpwnam(%s) failed.\n", curuser );
+ V_RET_FAIL( 0 );
+ }
+#endif
+ if (!p->pw_uid) {
+ if (!rootok && !td->allowRootLogin)
+ V_RET_FAIL( "Root logins are not allowed" );
+ return 1; /* don't deny root to log in */
+ }
+
+#ifdef USE_PAM
+
+ Debug( " pam_acct_mgmt() ...\n" );
+ pretc = pam_acct_mgmt( pamh, 0 );
+ ReInitErrorLog();
+ Debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pretc == PAM_NEW_AUTHTOK_REQD) {
+ pdata.usecur = FALSE;
+ pdata.gconv = conv_interact;
+ /* pam will have output a message already, so no PrepErrorGreet () */
+ if (gconv != conv_interact || pnopass) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ GSendInt( V_CHTOK_AUTH );
+ /* this cannot auth the wrong user, as only classic auths get here */
+ while (!doPAMAuth( PAMService, &pdata ))
+ if (pdata.abort)
+ return 0;
+ GSendInt( V_PRE_OK );
+ } else
+ GSendInt( V_CHTOK );
+ for (;;) {
+ Debug( " pam_chauthtok() ...\n" );
+ pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK );
+ ReInitErrorLog();
+ Debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) );
+ if (pdata.abort) {
+ pam_end( pamh, PAM_SUCCESS );
+ pamh = 0;
+ return 0;
+ }
+ if (pretc == PAM_SUCCESS)
+ break;
+ /* effectively there is only PAM_AUTHTOK_ERR */
+ GSendInt( V_FAIL );
+ }
+ if (curpass)
+ free( curpass );
+ curpass = newpass;
+ newpass = 0;
+ } else if (pretc != PAM_SUCCESS) {
+ pam_end( pamh, pretc );
+ pamh = 0;
+ V_RET_AUTH;
+ }
+
+#elif defined(_AIX) /* USE_PAM */
+
+ msg = NULL;
+ if (loginrestrictions( curuser,
+ ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN,
+ tty, &msg ) == -1)
+ {
+ Debug( "loginrestrictions() - %s\n", msg ? msg : "error" );
+ loginfailed( curuser, hostname, tty );
+ PrepErrorGreet();
+ if (msg) {
+ GSendInt( V_MSG_ERR );
+ GSendStr( msg );
+ }
+ GSendInt( V_AUTH );
+ return 0;
+ }
+ if (msg)
+ free( (void *)msg );
+
+#endif /* USE_PAM || _AIX */
+
+#ifndef _AIX
+
+# ifdef HAVE_SETUSERCONTEXT
+# ifdef HAVE_LOGIN_GETCLASS
+ lc = login_getclass( p->pw_class );
+# else
+ lc = login_getpwclass( p );
+# endif
+ if (!lc)
+ V_RET_FAIL( 0 );
+
+ p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell );
+# endif
+
+# ifndef USE_PAM
+
+/* restrict_expired */
+# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
+
+# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW))
+ if (sp)
+# endif
+ {
+
+# define DEFAULT_WARN (2L * 7L) /* Two weeks */
+
+ tim = time( NULL ) / 86400L;
+
+# ifdef HAVE_SETUSERCONTEXT
+ quietlog = login_getcapbool( lc, "hushlogin", 0 );
+ warntime = login_getcaptime( lc, "warnexpire",
+ DEFAULT_WARN * 86400L,
+ DEFAULT_WARN * 86400L ) / 86400L;
+# else
+ quietlog = 0;
+# ifdef USESHADOW
+ warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN;
+# else
+ warntime = DEFAULT_WARN;
+# endif
+# endif
+
+# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
+ if (p->pw_expire) {
+ expir = p->pw_expire / 86400L;
+# else
+ if (sp->sp_expire != -1) {
+ expir = sp->sp_expire;
+# endif
+ if (tim > expir) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "Your account has expired;"
+ " please contact your system administrator" );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (tim > (expir - warntime) && !quietlog) {
+ ASPrintf( &buf,
+ "Warning: your account will expire in %d day(s)",
+ expir - tim );
+ if (buf) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( buf );
+ free( buf );
+ }
+ }
+ }
+
+# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
+ if (p->pw_change) {
+ expir = p->pw_change / 86400L;
+# else
+ if (!sp->sp_lstchg) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "You are required to change your password immediately"
+ " (root enforced)" );
+ /* XXX todo password change */
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (sp->sp_max != -1) {
+ expir = sp->sp_lstchg + sp->sp_max;
+ if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "Your account has expired;"
+ " please contact your system administrator" );
+ GSendInt( V_FAIL );
+ LC_RET0;
+ }
+# endif
+ if (tim > expir) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_ERR );
+ GSendStr( "You are required to change your password immediately"
+ " (password aged)" );
+ /* XXX todo password change */
+ GSendInt( V_FAIL );
+ LC_RET0;
+ } else if (tim > (expir - warntime) && !quietlog) {
+ ASPrintf( &buf,
+ "Warning: your password will expire in %d day(s)",
+ expir - tim );
+ if (buf) {
+ PrepErrorGreet();
+ GSendInt( V_MSG_INFO );
+ GSendStr( buf );
+ free( buf );
+ }
+ }
+ }
+
+ }
+
+# 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 */
+
+/* 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 /* !_AIX */
+
+ return 1;
+
+}
+
+
+static const char *envvars[] = {
+ "TZ", /* SYSV and SVR4, but never hurts */
+#ifdef _AIX
+ "AUTHSTATE", /* for kerberos */
+#endif
+ NULL
+};
+
+
+#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 )
+{
+ if (setgid( gid ) < 0) {
+ LogError( "setgid(%d) (user %s) failed: %m\n", gid, name );
+ return 0;
+ }
+#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() */
+ return 1;
+}
+
+static int
+SetUid( const char *name, int uid )
+{
+ if (setuid( uid ) < 0) {
+ LogError( "setuid(%d) (user %s) failed: %m\n", uid, name );
+ return 0;
+ }
+ return 1;
+}
+
+static int
+SetUser( const char *name, int uid, int gid )
+{
+ if (SetGid( name, gid )) {
+ if (SetUid( name, uid ))
+ return 1;
+ resetGids();
+ }
+ return 0;
+}
+
+#if defined(SECURE_RPC) || defined(K5AUTH)
+static void
+NukeAuth( int len, const char *name )
+{
+ int i;
+
+ for (i = 0; i < td->authNum; i++)
+ if (td->authorizations[i]->name_length == len &&
+ !memcmp( td->authorizations[i]->name, name, len ))
+ {
+ memcpy( &td->authorizations[i], &td->authorizations[i+1],
+ sizeof(td->authorizations[i]) * (--td->authNum - i) );
+ break;
+ }
+}
+#endif
+
+static void
+mergeSessionArgs( int cansave )
+{
+ char *mfname;
+ const char *fname;
+ int i, needsave;
+
+ mfname = 0;
+ fname = ".dmrc";
+ if ((!curdmrc || newdmrc) && *dmrcDir)
+ if (StrApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 ))
+ fname = mfname;
+ needsave = 0;
+ if (!curdmrc) {
+ curdmrc = iniLoad( fname );
+ if (!curdmrc) {
+ StrDup( &curdmrc, "[Desktop]\nSession=default\n" );
+ needsave = 1;
+ }
+ }
+ if (newdmrc) {
+ curdmrc = iniMerge( curdmrc, newdmrc );
+ needsave = 1;
+ }
+ if (needsave && cansave)
+ if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) {
+ for (i = 0; mfname[i]; i++)
+ if (mfname[i] == '/') {
+ mfname[i] = 0;
+ mkdir( mfname, 0755 );
+ mfname[i] = '/';
+ }
+ iniSave( curdmrc, mfname );
+ }
+ if (mfname)
+ free( mfname );
+}
+
+static int removeAuth;
+#ifdef USE_PAM
+static int removeSession;
+static int removeCreds;
+#endif
+
+int
+StartClient()
+{
+ const char *home, *sessargs, *desksess;
+ char **env, *xma;
+ char **argv, *fname, *str;
+#ifdef USE_PAM
+ char **pam_env;
+# ifdef _AIX
+ char **saved_env;
+# endif
+ struct pam_conv pconv;
+ int pretc;
+#else
+# ifdef _AIX
+ char *msg;
+ char **theenv;
+ extern char **newenv; /* from libs.a, this is set up by setpenv */
+# endif
+#endif
+#ifdef HAVE_SETUSERCONTEXT
+ extern char **environ;
+#endif
+ char *failsafeArgv[2], *lname;
+ int i, pid, lfd;
+
+ if (StrCmp( dmrcuser, curuser )) {
+ if (curdmrc) { free( curdmrc ); curdmrc = 0; }
+ if (dmrcuser) { free( dmrcuser ); dmrcuser = 0; }
+ }
+
+#if defined(USE_PAM) || defined(_AIX)
+ if (!(p = getpwnam( curuser ))) {
+ LogError( "getpwnam(%s) failed.\n", curuser );
+ return 0;
+ }
+#endif
+
+#ifndef USE_PAM
+# ifdef _AIX
+ msg = NULL;
+ loginsuccess( curuser, hostname, tty, &msg );
+ if (msg) {
+ Debug( "loginsuccess() - %s\n", msg );
+ free( (void *)msg );
+ }
+# else /* _AIX */
+# if defined(KERBEROS) && !defined(NO_AFS)
+ if (krbtkfile[0] != '\0') {
+ if (k_hasafs()) {
+ if (k_setpag() == -1)
+ LogError( "setpag() for %s failed\n", curuser );
+ if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS)
+ LogError( "AFS Warning: %s\n", krb_get_err_text( ret ) );
+ }
+ }
+# endif /* KERBEROS && AFS */
+# endif /* _AIX */
+#endif /* !PAM */
+
+ curuid = p->pw_uid;
+ curgid = p->pw_gid;
+
+ env = baseEnv( curuser );
+ xma = 0;
+ if (td->ctrl.fpath && StrDup( &xma, td->ctrl.fpath )) {
+ if ((td->allowShutdown == SHUT_ALL ||
+ (td->allowShutdown == SHUT_ROOT && !curuser)) &&
+ StrApp( &xma, ",maysd", (char *)0 ))
+ {
+ if (td->allowNuke == SHUT_ALL ||
+ (td->allowNuke == SHUT_ROOT && !curuser))
+ StrApp( &xma, ",mayfn", (char *)0 );
+ StrApp( &xma, td->defSdMode == SHUT_FORCENOW ? ",fn" :
+ td->defSdMode == SHUT_TRYNOW ? ",tn" : ",sched",
+ (char *)0 );
+ }
+ if ((td->displayType & d_location) == dLocal && AnyReserveDisplays())
+ StrApp( &xma, ",rsvd", (char *)0 );
+ } else
+ StrDup( &xma, "true" );
+ StrApp( &xma, ",method=", curtype, (char *)0 );
+ if (td_setup)
+ StrApp( &xma, ",auto", (char *)0 );
+ if (xma) {
+ env = setEnv( env, "XDM_MANAGED", xma );
+ free( xma );
+ }
+ if (td->autoLock && cursource == PWSRC_AUTOLOGIN)
+ env = setEnv( env, "DESKTOP_LOCKED", "true" );
+ env = setEnv( env, "PATH", curuid ? td->userPath : td->systemPath );
+ env = setEnv( env, "SHELL", p->pw_shell );
+ env = setEnv( env, "HOME", p->pw_dir );
+#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS)
+ if (krbtkfile[0] != '\0')
+ env = setEnv( env, "KRBTKFILE", krbtkfile );
+#endif
+ userEnviron = inheritEnv( env, envvars );
+ env = systemEnv( p->pw_name );
+ systemEnviron = setEnv( env, "HOME", p->pw_dir );
+ Debug( "user environment:\n%[|''>'\n's"
+ "system environment:\n%[|''>'\n's"
+ "end of environments\n",
+ userEnviron,
+ systemEnviron );
+
+ /*
+ * for user-based authorization schemes,
+ * add the user to the server's allowed "hosts" list.
+ */
+ for (i = 0; i < td->authNum; i++) {
+#ifdef SECURE_RPC
+ if (td->authorizations[i]->name_length == 9 &&
+ !memcmp( td->authorizations[i]->name, "SUN-DES-1", 9 ))
+ {
+ XHostAddress addr;
+ char netname[MAXNETNAMELEN+1];
+ char domainname[MAXNETNAMELEN+1];
+
+ getdomainname( domainname, sizeof(domainname) );
+ user2netname( netname, curuid, domainname );
+ addr.family = FamilyNetname;
+ addr.length = strlen( netname );
+ addr.address = netname;
+ XAddHost( dpy, &addr );
+ }
+#endif
+#ifdef K5AUTH
+ if (td->authorizations[i]->name_length == 14 &&
+ !memcmp( td->authorizations[i]->name, "MIT-KERBEROS-5", 14 ))
+ {
+ /* Update server's auth file with user-specific info.
+ * Don't need to AddHost because X server will do that
+ * automatically when it reads the cache we are about
+ * to point it at.
+ */
+ XauDisposeAuth( td->authorizations[i] );
+ td->authorizations[i] =
+ Krb5GetAuthFor( 14, "MIT-KERBEROS-5", td->name );
+ SaveServerAuthorizations( td, td->authorizations, td->authNum );
+ }
+#endif
+ }
+
+ if (*dmrcDir)
+ 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;
+ 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();
+#if !defined(USE_PAM) && defined(USESHADOW) && !defined(_AIX)
+ endspent();
+#endif
+ ClearCloseOnFork( mstrtalk.pipe->wfd );
+ switch (pid = Fork()) {
+ 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 );
+ }
+
+ if (Setjmp( mstrtalk.errjmp ))
+ exit( 1 );
+ GSet( &mstrtalk );
+
+ setsid();
+ Signal( SIGINT, SIG_DFL );
+
+ /* Memory leaks are ok here as we exec() soon. */
+
+#if defined(USE_PAM) || !defined(_AIX)
+
+# ifdef USE_PAM
+ /* pass in environment variables set by libpam and modules it called */
+# ifndef _AIX
+ pam_env = pam_getenvlist( pamh );
+ ReInitErrorLog();
+# endif
+ if (pam_env)
+ for (; *pam_env; pam_env++)
+ userEnviron = putEnv( *pam_env, userEnviron );
+# endif
+
+# ifdef HAVE_SETLOGIN
+ if (setlogin( curuser ) < 0) {
+ LogError( "setlogin for %s failed: %m\n", curuser );
+ exit( 1 );
+ }
+# 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
+ if (!SetUser( curuser, curuid, curgid ))
+ exit( 1 );
+# endif
+
+# else /* !HAVE_SETUSERCONTEXT */
+
+ /*
+ * Destroy environment.
+ * We need to do this before setusercontext() because that may
+ * set or reset some environment variables.
+ */
+ if (!(environ = initStrArr( 0 )))
+ exit( 1 );
+
+ /*
+ * Set the user's credentials: uid, gid, groups,
+ * environment variables, resource limits, and umask.
+ */
+ 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 );
+ }
+
+ for (i = 0; environ[i]; i++)
+ userEnviron = putEnv( environ[i], userEnviron );
+
+# endif /* !HAVE_SETUSERCONTEXT */
+
+#else /* PAM || !_AIX */
+ /*
+ * Set the user's credentials: uid, gid, groups,
+ * audit classes, user limits, and umask.
+ */
+ if (setpcred( curuser, NULL ) == -1) {
+ LogError( "setpcred for %s failed: %m\n", curuser );
+ exit( 1 );
+ }
+
+ /*
+ * Set the users process environment. Store protected variables and
+ * obtain updated user environment list. This call will initialize
+ * global 'newenv'.
+ */
+ if (setpenv( curuser, PENV_INIT | PENV_ARGV | PENV_NOEXEC,
+ userEnviron, NULL ) != 0)
+ {
+ LogError( "Can't set %s's process environment\n", curuser );
+ exit( 1 );
+ }
+ userEnviron = newenv;
+
+#endif /* _AIX */
+
+ /*
+ * for user-based authorization schemes,
+ * use the password to get the user's credentials.
+ */
+#ifdef SECURE_RPC
+ /* do like "keylogin" program */
+ if (!curpass[0])
+ LogInfo( "No password for NIS provided.\n" );
+ else {
+ char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1];
+ int nameret, keyret;
+ int len;
+ int key_set_ok = 0;
+ struct key_netstarg netst;
+
+ nameret = getnetname( netname );
+ Debug( "user netname: %s\n", netname );
+ len = strlen( curpass );
+ if (len > 8)
+ bzero( curpass + 8, len - 8 );
+ keyret = getsecretkey( netname, secretkey, curpass );
+ Debug( "getsecretkey returns %d, key length %d\n",
+ keyret, strlen( secretkey ) );
+ netst.st_netname = netname;
+ memcpy( netst.st_priv_key, secretkey, HEXKEYBYTES );
+ memset( netst.st_pub_key, 0, HEXKEYBYTES );
+ if (key_setnet( &netst ) < 0)
+ Debug( "Could not set secret key.\n" );
+ /* is there a key, and do we have the right password? */
+ if (keyret == 1) {
+ if (*secretkey) {
+ keyret = key_setsecret( secretkey );
+ Debug( "key_setsecret returns %d\n", keyret );
+ if (keyret == -1)
+ LogError( "Failed to set NIS secret key\n" );
+ else
+ key_set_ok = 1;
+ } else {
+ /* found a key, but couldn't interpret it */
+ LogError( "Password incorrect for NIS principal %s\n",
+ nameret ? netname : curuser );
+ }
+ }
+ if (!key_set_ok)
+ NukeAuth( 9, "SUN-DES-1" );
+ bzero( secretkey, strlen( secretkey ) );
+ }
+#endif
+#ifdef K5AUTH
+ /* do like "kinit" program */
+ if (!curpass[0])
+ LogInfo( "No password for Kerberos5 provided.\n" );
+ else
+ if ((str = Krb5Init( curuser, curpass, td->name )))
+ userEnviron = setEnv( userEnviron, "KRB5CCNAME", str );
+ else
+ NukeAuth( 14, "MIT-KERBEROS-5" );
+#endif /* K5AUTH */
+ if (td->autoReLogin) {
+ GSendInt( D_ReLogin );
+ GSendStr( curuser );
+ GSendStr( curpass );
+ GSendStr( newdmrc );
+ }
+ if (curpass)
+ bzero( curpass, strlen( curpass ) );
+ SetUserAuthorization( td );
+ home = getEnv( userEnviron, "HOME" );
+ if (home) {
+ if (chdir( home ) < 0) {
+ LogError( "Cannot chdir to %s's home %s: %m, using /\n",
+ curuser, home );
+ home = 0;
+ userEnviron = setEnv( userEnviron, "HOME", "/" );
+ goto cdroot;
+ }
+ ASPrintf( &lname, td->clientLogFile, td->name );
+ if ((lfd = creat( lname, 0600 )) < 0) {
+ LogWarn( "Cannot create session log file %s: %m\n", lname );
+ free( lname );
+ goto tmperr;
+ }
+ } else {
+ cdroot:
+ chdir( "/" );
+ tmperr:
+ ASPrintf( &lname, "/tmp/xerr-%s-%s", curuser, td->name );
+ unlink( lname );
+ if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) < 0) {
+ LogError( "Cannot create fallback session log file %s: %m\n",
+ lname );
+ goto logerr;
+ }
+ }
+ dup2( lfd, 1 );
+ dup2( lfd, 2 );
+ close( lfd );
+ logerr:
+ free( lname );
+ if (!*dmrcDir)
+ mergeSessionArgs( home != 0 );
+ if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 )))
+ desksess = "failsafe"; /* only due to OOM */
+ GSendInt( D_User );
+ GSendInt( curuid );
+ GSendStr( curuser );
+ GSendStr( desksess );
+ close( mstrtalk.pipe->wfd );
+ userEnviron = setEnv( userEnviron, "DESKTOP_SESSION", desksess );
+ for (i = 0; td->sessionsDirs[i]; i++) {
+ fname = 0;
+ if (StrApp( &fname, td->sessionsDirs[i], "/", desksess, ".desktop", (char *)0 )) {
+ if ((str = iniLoad( fname ))) {
+ if (!StrCmp( iniEntry( str, "Desktop Entry", "Hidden", 0 ), "true" ) ||
+ !(sessargs = iniEntry( str, "Desktop Entry", "Exec", 0 )))
+ sessargs = "";
+ free( str );
+ free( fname );
+ goto gotit;
+ }
+ free( fname );
+ }
+ }
+ if (!strcmp( desksess, "failsafe" ) ||
+ !strcmp( desksess, "default" ) ||
+ !strcmp( desksess, "custom" ))
+ sessargs = desksess;
+ else
+ sessargs = "";
+ gotit:
+ if (!(argv = parseArgs( (char **)0, td->session )) ||
+ !(argv = addStrArr( argv, sessargs, -1 )))
+ exit( 1 );
+ if (argv[0] && *argv[0]) {
+ Debug( "executing session %\"[s\n", argv );
+ execute( argv, userEnviron );
+ LogError( "Session %\"s execution failed: %m\n", argv[0] );
+ } else
+ LogError( "Session has no command/arguments\n" );
+ failsafeArgv[0] = td->failsafeClient;
+ failsafeArgv[1] = 0;
+ execute( failsafeArgv, userEnviron );
+ LogError( "Failsafe client %\"s execution failed: %m\n",
+ failsafeArgv[0] );
+ exit( 1 );
+ case -1:
+ RegisterCloseOnFork( mstrtalk.pipe->wfd );
+ LogError( "Forking session on %s failed: %m\n", td->name );
+ return 0;
+ default:
+ RegisterCloseOnFork( mstrtalk.pipe->wfd );
+ Debug( "StartSession, fork succeeded %d\n", pid );
+ return pid;
+ }
+}
+
+void
+SessionExit( int status )
+{
+ int pid;
+#ifdef USE_PAM
+ 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 );
+
+ 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 );
+#endif /* K5AUTH */
+#if !defined(USE_PAM) && !defined(_AIX)
+# ifdef KERBEROS
+ if (krbtkfile[0]) {
+ (void)dest_tkt();
+# ifndef NO_AFS
+ if (k_hasafs())
+ (void)k_unlog();
+# endif
+ }
+# endif
+#endif /* !USE_PAM && !_AIX*/
+ }
+ exit( 0 );
+ case -1:
+ LogError( "Cannot clean up session: fork() failed: %m" );
+ break;
+ default:
+ Wait4( pid );
+ break;
+ }
+ }
+
+#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 );
+}
+
+int
+ReadDmrc()
+{
+ char *data, *fname = 0;
+ int len, pid, pfd[2], err;
+
+ if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam( dmrcuser )))
+ return GE_NoUser;
+
+ if (*dmrcDir) {
+ if (!StrApp( &fname, dmrcDir, "/", dmrcuser, ".dmrc", (char *)0 ))
+ return GE_Error;
+ if (!(curdmrc = iniLoad( fname ))) {
+ free( fname );
+ return GE_Ok;
+ }
+ free( fname );
+ return GE_NoFile;
+ }
+
+ if (!StrApp( &fname, p->pw_dir, "/.dmrc", (char *)0 ))
+ return GE_Error;
+ if (pipe( pfd ))
+ return GE_Error;
+ if ((pid = Fork()) < 0) {
+ close( pfd[0] );
+ close( pfd[1] );
+ return GE_Error;
+ }
+ if (!pid) {
+ if (!SetUser( p->pw_name, p->pw_uid, p->pw_gid ))
+ exit( 0 );
+ if (!(data = iniLoad( fname ))) {
+ static const int m1 = -1;
+ write( pfd[1], &m1, sizeof(int) );
+ exit( 0 );
+ }
+ len = strlen( data );
+ write( pfd[1], &len, sizeof(int) );
+ write( pfd[1], data, len + 1 );
+ exit( 0 );
+ }
+ close( pfd[1] );
+ free( fname );
+ err = GE_Error;
+ if (Reader( pfd[0], &len, sizeof(int) ) == sizeof(int)) {
+ if (len == -1)
+ err = GE_Denied;
+ else if ((curdmrc = Malloc( len + 1 ))) {
+ if (Reader( pfd[0], curdmrc, len + 1 ) == len + 1)
+ err = GE_Ok;
+ else {
+ free( curdmrc );
+ curdmrc = 0;
+ }
+ }
+ }
+ close( pfd[0] );
+ (void)Wait4( pid );
+ return err;
+}