You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1883 lines
45 KiB
1883 lines
45 KiB
/*
|
|
|
|
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>
|
|
|
|
#ifdef WITH_CONSOLE_KIT
|
|
#include "consolekit.h"
|
|
#endif
|
|
|
|
#define AU_FAILED 0
|
|
#define AU_SUCCESS 1
|
|
#ifdef HAVE_LIBAUDIT
|
|
#include <libaudit.h>
|
|
#else
|
|
#define log_to_audit_system(l,h,d,s) do { ; } while (0)
|
|
#endif
|
|
|
|
/*
|
|
* 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 );
|
|
// WARNING
|
|
// This is far from foolproof, but it's the best we can do at this time...
|
|
// Try to detect PIN entry requests
|
|
if (strstr(msg[count]->msg, "PIN")) {
|
|
reply[count].resp = pd->gconv(GCONV_HIDDEN, msg[count]->msg);
|
|
}
|
|
else {
|
|
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
|
|
|
|
/**
|
|
* log_to_audit_system:
|
|
* @login: Name of user
|
|
* @hostname: Name of host machine
|
|
* @tty: Name of display
|
|
* @success: 1 for success, 0 for failure
|
|
*
|
|
* Logs the success or failure of the login attempt with the linux kernel
|
|
* audit system. The intent is to capture failed events where the user
|
|
* fails authentication or otherwise is not permitted to login. There are
|
|
* many other places where pam could potentially fail and cause login to
|
|
* fail, but these are system failures rather than the signs of an account
|
|
* being hacked.
|
|
*
|
|
* Returns nothing.
|
|
*/
|
|
|
|
#ifdef HAVE_LIBAUDIT
|
|
static void
|
|
log_to_audit_system (const char *loginname,
|
|
const char *hostname,
|
|
const char *tty,
|
|
int success)
|
|
{
|
|
struct passwd *pw;
|
|
char buf[64];
|
|
int audit_fd;
|
|
|
|
audit_fd = audit_open();
|
|
if (loginname)
|
|
pw = getpwnam(loginname);
|
|
else {
|
|
loginname = "unknown";
|
|
pw = NULL;
|
|
}
|
|
Debug("log_to_audit %p %s\n", pw, loginname);
|
|
|
|
if (pw) {
|
|
snprintf(buf, sizeof(buf), "uid=%d", pw->pw_uid);
|
|
audit_log_user_message(audit_fd, AUDIT_USER_LOGIN,
|
|
buf, hostname, NULL, tty, (int)success);
|
|
} else {
|
|
snprintf(buf, sizeof(buf), "acct=%s", loginname);
|
|
audit_log_user_message(audit_fd, AUDIT_USER_LOGIN,
|
|
buf, hostname, NULL, tty, (int)success);
|
|
}
|
|
close(audit_fd);
|
|
}
|
|
#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
|
|
pretc = pam_putenv( pamh, "XDG_SESSION_CLASS=greeter" );
|
|
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) {
|
|
/* Log the failed login attempt */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
|
|
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 if (!strcmp( curtype, "pam" )) {
|
|
psrv = PAMService;
|
|
pdata.usecur = FALSE;
|
|
} 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" ) || !strcmp(curtype, "pam")) {
|
|
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" );
|
|
/* Log the failed login attempt */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
|
|
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;
|
|
/* Log the failed login attempt */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
|
|
/* 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" );
|
|
/* Log the failed login attempt */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
|
|
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" );
|
|
/* Log the failed login attempt */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
|
|
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" );
|
|
/* Log the failed login attempt */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
|
|
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" );
|
|
/* Log the failed login attempt */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
|
|
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" );
|
|
/* Log the failed login attempt */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_FAILED);
|
|
}
|
|
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 );
|
|
if (setgid(0) != 0) {
|
|
LogError("setgid(0) failed\n");
|
|
}
|
|
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
|
|
|
|
#ifdef WITH_CONSOLE_KIT
|
|
int
|
|
StartClient( const char *ck_session_cookie )
|
|
#else
|
|
int
|
|
StartClient()
|
|
#endif
|
|
{
|
|
const char *home, *sessargs, *desksess, *deskname;
|
|
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 (cursource == PWSRC_AUTOLOGIN)
|
|
env = setEnv (env, "TDM_AUTOLOGIN", curuser);
|
|
#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS)
|
|
if (krbtkfile[0] != '\0')
|
|
env = setEnv( env, "KRBTKFILE", krbtkfile );
|
|
#endif
|
|
#ifdef WITH_CONSOLE_KIT
|
|
if (ck_session_cookie != NULL) {
|
|
env = setEnv ( env, "XDG_SESSION_COOKIE", ck_session_cookie );
|
|
}
|
|
#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 */
|
|
|
|
/* Login succeeded */
|
|
log_to_audit_system (curuser, td->remoteHost, td->name, AU_SUCCESS);
|
|
|
|
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 "tdeinit" 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:
|
|
if (chdir( "/" ) != 0) {
|
|
LogError( "Cannot change directory to %s\n", "/" );
|
|
goto logerr;
|
|
}
|
|
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 );
|
|
userEnviron = setEnv( userEnviron, "XDG_SESSION_DESKTOP", 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 = "";
|
|
deskname = iniEntry( str, "Desktop Entry", "DesktopNames", 0 );
|
|
if (deskname) {
|
|
userEnviron = setEnv( userEnviron, "XDG_CURRENT_DESKTOP", deskname );
|
|
}
|
|
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;
|
|
if (write(pfd[1], &m1, sizeof(int)) < 0) {
|
|
return GE_Error;
|
|
}
|
|
exit(0);
|
|
}
|
|
len = strlen( data );
|
|
if (write(pfd[1], &len, sizeof(int)) < 0) {
|
|
return GE_Error;
|
|
}
|
|
if (write(pfd[1], data, len + 1) < 0) {
|
|
return GE_Error;
|
|
}
|
|
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;
|
|
}
|