3 Copyright 1988, 1998 The Open Group
4 Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org>
5 Copyright 2008 Juuso Alasuutari <juuso.alasuutari@gmail.com>
7 Permission to use, copy, modify, distribute, and sell this software and its
8 documentation for any purpose is hereby granted without fee, provided that
9 the above copyright notice appear in all copies and that both that
10 copyright notice and this permission notice appear in supporting
13 The above copyright notice and this permission notice shall be included
14 in all copies or substantial portions of the Software.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
19 IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
20 OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
21 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
22 OTHER DEALINGS IN THE SOFTWARE.
24 Except as contained in this notice, the name of a copyright holder shall
25 not be used in advertising or otherwise to promote the sale, use or
26 other dealings in this Software without prior written authorization
27 from the copyright holder.
32 * xdm - display manager daemon
33 * Author: Keith Packard, MIT X Consortium
35 * user verification and session initiation.
46 # include <rpc/key_prot.h>
47 extern int key_setnet( struct key_netstarg
*arg
);
50 # include <krb5/krb5.h>
52 #ifdef HAVE_SETUSERCONTEXT
53 # include <login_cap.h>
56 # ifdef HAVE_PAM_PAM_APPL_H
57 # include <pam/pam_appl.h>
59 # include <security/pam_appl.h>
61 #elif defined(_AIX) /* USE_PAM */
64 extern int loginrestrictions( const char *Name
, const int Mode
, const char *Tty
, char **Msg
);
65 extern int loginfailed( const char *User
, const char *Host
, const char *Tty
);
66 extern int loginsuccess( const char *User
, const char *Host
, const char *Tty
, char **Msg
);
67 #else /* USE_PAM || _AIX */
69 # include <sys/param.h>
76 # include <sys/types.h>
80 #endif /* USE_PAM || _AIX */
81 #ifdef HAVE_CKCONNECTOR
82 # include <ck-connector.h>
83 # include <dbus/dbus.h>
91 * Session data, mostly what struct verify_info was for
107 static struct passwd
*p
;
108 #ifdef HAVE_SETUSERCONTEXT
109 # ifdef HAVE_LOGIN_GETCLASS
112 struct login_cap
*lc
;
116 static pam_handle_t
*pamh
;
119 static char tty
[16], hostname
[100];
122 static struct spwd
*sp
;
125 static char krbtkfile
[MAXPATHLEN
];
130 displayStr( int lv
, const char *msg
)
138 #if !defined(USE_PAM) && (defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW))
140 displayMsg( int lv
, const char *msg
, ... )
145 va_start( args
, msg
);
146 VASPrintf( &ae
, msg
, args
);
149 displayStr( lv
, ae
);
156 # define _ENDUSERDB , enduserdb()
160 #ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
161 # define _ENDSPENT , endspent()
165 #define END_ENT endpwent() _ENDSPENT _ENDUSERDB
175 wipeStr( curpass ); \
182 prepareErrorGreet(); \
183 gSendInt( V_AUTH ); \
187 #define V_RET_FAIL(m) \
189 displayStr( V_MSG_ERR, m ); \
190 gSendInt( V_FAIL ); \
196 # ifndef PAM_MESSAGE_CONST
197 typedef struct pam_message pam_message_type
;
198 typedef void *pam_gi_type
;
200 typedef const struct pam_message pam_message_type
;
201 typedef const void *pam_gi_type
;
211 PAM_conv( int num_msg
,
212 pam_message_type
**msg
,
213 struct pam_response
**resp
,
217 struct pam_response
*reply
;
218 struct pam_data
*pd
= (struct pam_data
*)appdata_ptr
;
220 if (!(reply
= Calloc( num_msg
, sizeof(*reply
) )))
224 debug( "PAM_conv\n" );
225 for (count
= 0; count
< num_msg
; count
++)
226 switch (msg
[count
]->msg_style
) {
228 debug( " PAM_TEXT_INFO: %s\n", msg
[count
]->msg
);
229 displayStr( inAuth
? V_MSG_INFO_AUTH
: V_MSG_INFO
, msg
[count
]->msg
);
232 debug( " PAM_ERROR_MSG: %s\n", msg
[count
]->msg
);
233 displayStr( V_MSG_ERR
, msg
[count
]->msg
);
236 /* could do better error handling here, but see below ... */
238 switch (msg
[count
]->msg_style
) {
239 /* case PAM_PROMPT_ECHO_ON: cannot happen */
240 case PAM_PROMPT_ECHO_OFF
:
241 debug( " PAM_PROMPT_ECHO_OFF (usecur): %s\n", msg
[count
]->msg
);
243 pd
->gconv( GCONV_PASS
, 0 );
244 strDup( &reply
[count
].resp
, curpass
);
247 logError( "Unknown PAM message style <%d>\n", msg
[count
]->msg_style
);
251 switch (msg
[count
]->msg_style
) {
252 case PAM_PROMPT_ECHO_ON
:
253 debug( " PAM_PROMPT_ECHO_ON: %s\n", msg
[count
]->msg
);
254 reply
[count
].resp
= pd
->gconv( GCONV_NORMAL
, msg
[count
]->msg
);
256 case PAM_PROMPT_ECHO_OFF
:
257 debug( " PAM_PROMPT_ECHO_OFF: %s\n", msg
[count
]->msg
);
258 reply
[count
].resp
= pd
->gconv( GCONV_HIDDEN
, msg
[count
]->msg
);
260 #ifdef PAM_BINARY_PROMPT
261 case PAM_BINARY_PROMPT
:
262 debug( " PAM_BINARY_PROMPT\n" );
263 reply
[count
].resp
= pd
->gconv( GCONV_BINARY
, msg
[count
]->msg
);
267 logError( "Unknown PAM message style <%d>\n", msg
[count
]->msg_style
);
271 if (!reply
[count
].resp
) {
272 debug( " PAM_conv aborted\n" );
276 reply
[count
].resp_retcode
= PAM_SUCCESS
; /* unused in linux-pam */
278 debug( " PAM_conv success\n" );
283 for (; count
>= 0; count
--)
284 if (reply
[count
].resp
)
285 switch (msg
[count
]->msg_style
) {
286 case PAM_PROMPT_ECHO_OFF
:
287 wipeStr( reply
[count
].resp
);
289 #ifdef PAM_BINARY_PROMPT
290 case PAM_BINARY_PROMPT
: /* Don't know length, so how wipe? */
292 case PAM_PROMPT_ECHO_ON
:
293 free( reply
[count
].resp
);
300 # ifdef PAM_FAIL_DELAY
302 fail_delay( int retval ATTR_UNUSED
, unsigned usec_delay ATTR_UNUSED
,
303 void *appdata_ptr ATTR_UNUSED
)
308 /* Inspired by Eamon Walsh's patch for gdm. */
309 static struct pam_xauth_data
*
310 getPAMXauthData( const char *xauth_file
)
314 struct pam_xauth_data
*ret
;
316 if (!(fp
= fopen( xauth_file
, "r" )))
319 auth
= XauReadAuth( fp
);
324 if ((ret
= malloc( sizeof(*ret
) + auth
->name_length
+ 1 + auth
->data_length
))) {
325 ret
->name
= (char *)ret
+ sizeof(*ret
);
326 ret
->namelen
= auth
->name_length
;
327 memcpy( ret
->name
, auth
->name
, auth
->name_length
);
328 ret
->name
[ret
->namelen
] = 0;
329 ret
->data
= ret
->name
+ ret
->namelen
+ 1;
330 ret
->datalen
= auth
->data_length
;
331 memcpy( ret
->data
, auth
->data
, auth
->data_length
);
334 XauDisposeAuth( auth
);
340 doPAMAuth( const char *psrv
, struct pam_data
*pdata
)
343 struct pam_xauth_data
*pam_xauth
;
346 struct pam_conv pconv
;
349 pdata
->abort
= False
;
350 pconv
.conv
= PAM_conv
;
351 pconv
.appdata_ptr
= (void *)pdata
;
352 debug( " PAM service %s\n", psrv
);
353 if ((pretc
= pam_start( psrv
, curuser
, &pconv
, &pamh
)) != PAM_SUCCESS
)
355 if ((pretc
= pam_set_item( pamh
, PAM_TTY
, td
->name
)) != PAM_SUCCESS
) {
357 pam_end( pamh
, pretc
);
361 logError( "PAM error: %s\n", pam_strerror( 0, pretc
) );
364 if ((td
->displayType
& d_location
) == dForeign
) {
365 char *cp
= strchr( td
->name
, ':' );
367 pretc
= pam_set_item( pamh
, PAM_RHOST
, td
->name
);
369 if (pretc
!= PAM_SUCCESS
)
372 # ifdef __sun__ /* Only Solaris <= 9, but checking it does not seem worth it. */
373 else if ((pretc
= pam_set_item( pamh
, PAM_RHOST
, 0 )) != PAM_SUCCESS
)
377 if ((pretc
= pam_set_item( pamh
, PAM_XDISPLAY
,
378 displayName( td
) )) != PAM_SUCCESS
)
380 if (td
->authFile
&& (pam_xauth
= getPAMXauthData( td
->authFile
))) {
381 pretc
= pam_set_item( pamh
, PAM_XAUTHDATA
, pam_xauth
);
383 if (pretc
!= PAM_SUCCESS
)
387 pam_set_item( pamh
, PAM_USER_PROMPT
, (void *)"Username:" );
388 # ifdef PAM_FAIL_DELAY
389 pam_set_item( pamh
, PAM_FAIL_DELAY
, (void *)fail_delay
);
394 debug( " pam_authenticate() ...\n" );
395 pretc
= pam_authenticate( pamh
,
396 td
->allowNullPasswd
? 0 : PAM_DISALLOW_NULL_AUTHTOK
);
398 debug( " pam_authenticate() returned: %s\n", pam_strerror( pamh
, pretc
) );
401 pam_end( pamh
, PAM_SUCCESS
);
406 * Do not even *think* about making that unconditional.
407 * If a module thinks it needs to normalize user names (hello LDAP),
408 * feed it with normalized data in the first place and/or do some
409 * final normalization in startClient(). If that turns out impossible,
410 * the module needs an own conversation plugin which does not cause
414 debug( " asking PAM for user ...\n" );
415 pam_get_item( pamh
, PAM_USER
, &pitem
);
417 strDup( &curuser
, (const char *)pitem
);
418 gSendInt( V_PUT_USER
);
421 if (pretc
!= PAM_SUCCESS
) {
423 case PAM_USER_UNKNOWN
:
425 case PAM_MAXTRIES
: /* should handle this better ... */
426 case PAM_AUTHINFO_UNAVAIL
: /* returned for unknown users ... bogus */
427 pam_end( pamh
, pretc
);
431 pam_end( pamh
, pretc
);
442 #if defined(USE_PAM) || defined(_AIX)
443 isNoPassAllowed( const char *un
)
446 # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
450 isNoPassAllowed( struct passwd
*pw
)
457 #if defined(USE_PAM) || defined(_AIX)
462 if (cursource
!= PWSRC_MANUAL
)
465 #if defined(USE_PAM) || defined(_AIX)
466 /* Give nss_ldap, etc. a chance to normalize (uppercase) the name. */
467 if (!(pw
= getpwnam( un
)) ||
468 pw
->pw_passwd
[0] == '!' || pw
->pw_passwd
[0] == '*')
470 # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
471 if ((spw
= getspnam( un
)) &&
472 (spw
->sp_pwdp
[0] == '!' || spw
->sp_pwdp
[0] == '*'))
477 for (hg
= False
, fp
= td
->noPassUsers
; *fp
; fp
++)
480 else if (!strcmp( pw
->pw_name
, *fp
))
482 else if (!strcmp( "*", *fp
) && pw
->pw_uid
)
486 for (setgrent(); (gr
= getgrent()); )
487 for (fp
= td
->noPassUsers
; *fp
; fp
++)
488 if (**fp
== '@' && !strcmp( gr
->gr_name
, *fp
+ 1 )) {
489 if (pw
->pw_gid
== gr
->gr_gid
) {
493 for (; *gr
->gr_mem
; gr
->gr_mem
++)
494 if (!strcmp( pw
->pw_name
, *gr
->gr_mem
)) {
505 #if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT)
506 # define LC_RET0 do { login_close(lc); V_RET; } while(0)
508 # define LC_RET0 V_RET
512 verify( GConvFunc gconv
, int rootok
)
516 struct pam_data pdata
;
527 # ifdef HAVE_GETUSERSHELL
530 # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
531 int tim
, expir
, warntime
, quietlog
;
535 debug( "verify ...\n" );
540 if (!strcmp( curtype
, "classic" )) {
541 if (!gconv( GCONV_USER
, 0 ))
543 if (isNoPassAllowed( curuser
)) {
544 gconv( GCONV_PASS_ND
, 0 );
547 sprintf( psrvb
, "%.31s-np", PAMService
);
555 sprintf( psrvb
, "%.31s-%.31s", PAMService
, curtype
);
557 pdata
.usecur
= False
;
560 if (!doPAMAuth( psrv
, &pdata
))
565 if ((td
->displayType
& d_location
) == dForeign
) {
567 strncpy( hostname
, td
->name
, sizeof(hostname
) - 1 );
568 hostname
[sizeof(hostname
)-1] = '\0';
569 if ((tmpch
= strchr( hostname
, ':' )))
574 /* tty names should only be 15 characters long */
576 for (i
= 0; i
< 15 && td
->name
[i
]; i
++) {
577 if (td
->name
[i
] == ':' || td
->name
[i
] == '.')
580 tty
[i
] = td
->name
[i
];
584 memcpy( tty
, "/dev/xdm/", 9 );
585 for (i
= 0; i
< 6 && td
->name
[i
]; i
++) {
586 if (td
->name
[i
] == ':' || td
->name
[i
] == '.')
589 tty
[9 + i
] = td
->name
[i
];
594 if (!strcmp( curtype
, "classic" )) {
595 if (!gconv( GCONV_USER
, 0 ))
597 if (isNoPassAllowed( curuser
)) {
598 gconv( GCONV_PASS_ND
, 0 );
600 debug( "accepting despite empty password\n" );
604 if (!gconv( GCONV_PASS
, 0 ))
607 if ((i
= authenticate( curuser
, curpass
, &reenter
, &msg
))) {
608 debug( "authenticate() failed: %s\n", msg
);
611 loginfailed( curuser
, hostname
, tty
);
612 if (i
== ENOENT
|| i
== ESAD
)
618 logError( "authenticate() requests more data: %s\n", msg
);
622 } else if (!strcmp( curtype
, "generic" )) {
623 if (!gconv( GCONV_USER
, 0 ))
627 if ((i
= authenticate( curuser
, curret
, &reenter
, &msg
))) {
628 debug( "authenticate() failed: %s\n", msg
);
631 loginfailed( curuser
, hostname
, tty
);
632 if (i
== ENOENT
|| i
== ESAD
)
641 if (!(curret
= gconv( GCONV_HIDDEN
, msg
)))
646 logError( "Unsupported authentication type %\"s requested\n", curtype
);
650 displayStr( V_MSG_INFO
, msg
);
658 if (strcmp( curtype
, "classic" )) {
659 logError( "Unsupported authentication type %\"s requested\n", curtype
);
663 if (!gconv( GCONV_USER
, 0 ))
666 if (!(p
= getpwnam( curuser
))) {
667 debug( "getpwnam() failed.\n" );
668 gconv( GCONV_PASS
, 0 );
671 if (p
->pw_passwd
[0] == '!' || p
->pw_passwd
[0] == '*') {
672 debug( "account is locked\n" );
673 gconv( GCONV_PASS
, 0 );
678 if ((sp
= getspnam( curuser
))) {
679 p
->pw_passwd
= sp
->sp_pwdp
;
680 if (p
->pw_passwd
[0] == '!' || p
->pw_passwd
[0] == '*') {
681 debug( "account is locked\n" );
682 gconv( GCONV_PASS
, 0 );
686 debug( "getspnam() failed: %m. Are you root?\n" );
689 if (!*p
->pw_passwd
) {
690 if (!td
->allowNullPasswd
) {
691 debug( "denying user with empty password\n" );
692 gconv( GCONV_PASS
, 0 );
698 if (isNoPassAllowed( p
)) {
700 gconv( GCONV_PASS_ND
, 0 );
702 debug( "accepting password-less login\n" );
706 if (!gconv( GCONV_PASS
, 0 ))
712 char realm
[REALM_SZ
];
714 if (krb_get_lrealm( realm
, 1 )) {
715 logError( "Cannot get KerberosIV realm.\n" );
719 sprintf( krbtkfile
, "%s.%.*s", TKT_ROOT
, MAXPATHLEN
- strlen( TKT_ROOT
) - 2, td
->name
);
720 krb_set_tkt_string( krbtkfile
);
723 ret
= krb_verify_user( curuser
, "", realm
, curpass
, 1, "rcmd" );
724 if (ret
== KSUCCESS
) {
725 chown( krbtkfile
, p
->pw_uid
, p
->pw_gid
);
726 debug( "KerberosIV verify succeeded\n" );
728 } else if (ret
!= KDC_PR_UNKNOWN
&& ret
!= SKDC_CANT
) {
729 logError( "KerberosIV verification failure %\"s for %s\n",
730 krb_get_err_text( ret
), curuser
);
734 debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret
) );
737 # endif /* KERBEROS */
739 # if defined(ultrix) || defined(__ultrix__)
740 if (authenticate_user( p
, curpass
, NULL
) < 0)
741 # elif defined(HAVE_PW_ENCRYPT)
742 if (strcmp( pw_encrypt( curpass
, p
->pw_passwd
), p
->pw_passwd
))
743 # elif defined(HAVE_CRYPT)
744 if (strcmp( crypt( curpass
, p
->pw_passwd
), p
->pw_passwd
))
746 if (strcmp( curpass
, p
->pw_passwd
))
749 debug( "password verify failed\n" );
755 #endif /* !defined(USE_PAM) && !defined(_AIX) */
757 debug( "restrict %s ...\n", curuser
);
759 #if defined(USE_PAM) || defined(_AIX)
760 if (!(p
= getpwnam( curuser
))) {
761 logError( "getpwnam(%s) failed.\n", curuser
);
766 if (!rootok
&& !td
->allowRootLogin
)
767 V_RET_FAIL( "Root logins are not allowed" );
770 return True
; /* don't deny root to log in */
775 debug( " pam_acct_mgmt() ...\n" );
776 pretc
= pam_acct_mgmt( pamh
, 0 );
778 debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh
, pretc
) );
779 if (pretc
== PAM_NEW_AUTHTOK_REQD
) {
780 pdata
.usecur
= False
;
781 pdata
.gconv
= conv_interact
;
782 /* pam will have output a message already, so no prepareErrorGreet() */
783 if (gconv
!= conv_interact
|| pnopass
) {
784 pam_end( pamh
, PAM_SUCCESS
);
786 gSendInt( V_CHTOK_AUTH
);
787 /* this cannot auth the wrong user, as only classic auths get here */
788 while (!doPAMAuth( PAMService
, &pdata
))
791 gSendInt( V_PRE_OK
);
796 debug( " pam_chauthtok() ...\n" );
797 pretc
= pam_chauthtok( pamh
, PAM_CHANGE_EXPIRED_AUTHTOK
);
799 debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh
, pretc
) );
802 pam_end( pamh
, PAM_SUCCESS
);
806 if (pretc
== PAM_SUCCESS
)
808 /* effectively there is only PAM_AUTHTOK_ERR */
814 } else if (pretc
!= PAM_SUCCESS
) {
815 pam_end( pamh
, pretc
);
820 #elif defined(_AIX) /* USE_PAM */
823 if (loginrestrictions( curuser
,
824 ((td
->displayType
& d_location
) == dForeign
) ? S_RLOGIN
: S_LOGIN
,
827 debug( "loginrestrictions() - %s\n", msg
? msg
: "error" );
828 loginfailed( curuser
, hostname
, tty
);
831 displayStr( V_MSG_ERR
, msg
);
840 #endif /* USE_PAM || _AIX */
844 # ifdef HAVE_SETUSERCONTEXT
845 # ifdef HAVE_LOGIN_GETCLASS
846 lc
= login_getclass( p
->pw_class
);
848 lc
= login_getpwclass( p
);
853 p
->pw_shell
= login_getcapstr( lc
, "shell", p
->pw_shell
, p
->pw_shell
);
858 /* restrict_expired */
859 # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
861 # if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW))
866 # define DEFAULT_WARN (2L * 7L) /* Two weeks */
868 tim
= time( NULL
) / 86400L;
870 # ifdef HAVE_SETUSERCONTEXT
871 quietlog
= login_getcapbool( lc
, "hushlogin", False
);
872 warntime
= login_getcaptime( lc
, "warnexpire",
873 DEFAULT_WARN
* 86400L,
874 DEFAULT_WARN
* 86400L ) / 86400L;
878 warntime
= sp
->sp_warn
!= -1 ? sp
->sp_warn
: DEFAULT_WARN
;
880 warntime
= DEFAULT_WARN
;
884 # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
886 expir
= p
->pw_expire
/ 86400L;
888 if (sp
->sp_expire
!= -1) {
889 expir
= sp
->sp_expire
;
892 displayStr( V_MSG_ERR
,
893 "Your account has expired;"
894 " please contact your system administrator" );
897 } else if (tim
> (expir
- warntime
) && !quietlog
) {
898 displayMsg( V_MSG_INFO
,
899 "Warning: your account will expire in %d day(s)",
904 # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
906 expir
= p
->pw_change
/ 86400L;
908 if (!sp
->sp_lstchg
) {
909 displayStr( V_MSG_ERR
,
910 "You are required to change your password immediately"
911 " (root enforced)" );
912 /* XXX todo password change */
915 } else if (sp
->sp_max
!= -1) {
916 expir
= sp
->sp_lstchg
+ sp
->sp_max
;
917 if (sp
->sp_inact
!= -1 && tim
> expir
+ sp
->sp_inact
) {
918 displayStr( V_MSG_ERR
,
919 "Your account has expired;"
920 " please contact your system administrator" );
926 displayStr( V_MSG_ERR
,
927 "You are required to change your password immediately"
928 " (password aged)" );
929 /* XXX todo password change */
932 } else if (tim
> (expir
- warntime
) && !quietlog
) {
933 displayMsg( V_MSG_INFO
,
934 "Warning: your password will expire in %d day(s)",
941 # endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */
943 /* restrict_nologin */
944 # ifndef _PATH_NOLOGIN
945 # define _PATH_NOLOGIN "/etc/nologin"
949 # ifdef HAVE_SETUSERCONTEXT
950 /* Do we ignore a nologin file? */
951 !login_getcapbool( lc
, "ignorenologin", False
)) &&
952 (!stat( (nolg
= login_getcapstr( lc
, "nologin", "", NULL
)), &st
) ||
954 !stat( (nolg
= _PATH_NOLOGIN
), &st
)))
956 if (st
.st_size
&& (fd
= open( nolg
, O_RDONLY
)) >= 0) {
957 if ((buf
= Malloc( st
.st_size
+ 1 ))) {
958 if (read( fd
, buf
, st
.st_size
) == st
.st_size
) {
961 displayStr( V_MSG_ERR
, buf
);
970 displayStr( V_MSG_ERR
,
971 "Logins are not allowed at the moment.\nTry again later" );
977 # if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK)
978 if (!auth_timeok( lc
, time( NULL
) )) {
979 displayStr( V_MSG_ERR
,
980 "You are not allowed to login at the moment" );
986 # ifdef HAVE_GETUSERSHELL
988 if (!(s
= getusershell())) {
989 debug( "shell not in /etc/shells\n" );
991 V_RET_FAIL( "Your login shell is not listed in /etc/shells" );
993 if (!strcmp( s
, p
->pw_shell
)) {
1000 # endif /* !USE_PAM */
1002 /* restrict_nohome */
1003 # ifdef HAVE_SETUSERCONTEXT
1004 if (login_getcapbool( lc
, "requirehome", False
)) {
1006 if (!*p
->pw_dir
|| stat( p
->pw_dir
, &st
) || st
.st_uid
!= p
->pw_uid
) {
1007 displayStr( V_MSG_ERR
, "Home folder not available" );
1021 static const char *envvars
[] = {
1022 "TZ", /* SYSV and SVR4, but never hurts */
1024 "AUTHSTATE", /* for kerberos */
1030 #if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1031 static int num_saved_gids
;
1032 static gid_t
*saved_gids
;
1037 num_saved_gids
= getgroups( 0, 0 );
1038 if (!(saved_gids
= Malloc( sizeof(gid_t
) * num_saved_gids
)))
1040 if (getgroups( num_saved_gids
, saved_gids
) < 0) {
1041 logError( "saving groups failed: %m\n" );
1050 if (setgroups( num_saved_gids
, saved_gids
) < 0) {
1051 logError( "restoring groups failed: %m\n" );
1054 if (setgid( p
->pw_gid
) < 0) {
1055 logError( "restoring gid failed: %m\n" );
1060 #endif /* USE_PAM && HAVE_INITGROUPS */
1065 #ifdef HAVE_INITGROUPS
1066 if (setgroups( 0, &p
->pw_gid
/* anything */ ) < 0) {
1067 logError( "restoring groups failed: %m\n" );
1071 if (setgid( 0 ) < 0) {
1072 logError( "restoring gid failed: %m\n" );
1079 setGid( const char *name
, int gid
)
1081 if (setgid( gid
) < 0) {
1082 logError( "setgid(%d) (user %s) failed: %m\n", gid
, name
);
1085 #ifdef HAVE_INITGROUPS
1086 if (initgroups( name
, gid
) < 0) {
1087 logError( "initgroups for %s failed: %m\n", name
);
1091 #endif /* QNX4 doesn't support multi-groups, no initgroups() */
1096 setUid( const char *name
, int uid
)
1098 if (setuid( uid
) < 0) {
1099 logError( "setuid(%d) (user %s) failed: %m\n", uid
, name
);
1106 setUser( const char *name
, int uid
, int gid
)
1108 if (setGid( name
, gid
)) {
1109 if (setUid( name
, uid
))
1116 #if defined(SECURE_RPC) || defined(K5AUTH)
1118 nukeAuth( int len
, const char *name
)
1122 for (i
= 0; i
< td
->authNum
; i
++)
1123 if (td
->authorizations
[i
]->name_length
== len
&&
1124 !memcmp( td
->authorizations
[i
]->name
, name
, len
))
1126 memcpy( &td
->authorizations
[i
], &td
->authorizations
[i
+1],
1127 sizeof(td
->authorizations
[i
]) * (--td
->authNum
- i
) );
1134 mergeSessionArgs( int cansave
)
1142 if ((!curdmrc
|| newdmrc
) && *dmrcDir
)
1143 if (strApp( &mfname
, dmrcDir
, "/", curuser
, fname
, (char *)0 ))
1147 curdmrc
= iniLoad( fname
);
1149 strDup( &curdmrc
, "[Desktop]\nSession=default\n" );
1154 curdmrc
= iniMerge( curdmrc
, newdmrc
);
1157 if (needsave
&& cansave
)
1158 if (!iniSave( curdmrc
, fname
) && errno
== ENOENT
&& mfname
) {
1159 for (i
= 0; mfname
[i
]; i
++)
1160 if (mfname
[i
] == '/') {
1162 mkdir( mfname
, 0755 );
1165 iniSave( curdmrc
, mfname
);
1172 createClientLog( const char *log
)
1174 char randstr
[32], *randstrp
= 0, *lname
;
1178 struct expando macros
[] = {
1179 { 'd', 0, td
->name
},
1180 { 'u', 0, curuser
},
1181 { 'r', 0, randstrp
},
1184 if (!(lname
= expandMacros( log
, macros
)))
1187 if ((lfd
= open( lname
, O_WRONLY
|O_CREAT
|O_EXCL
, 0600 )) >= 0) {
1194 if (errno
!= EEXIST
|| !macros
[2].uses
) {
1198 logInfo( "Session log file %s not usable, trying another one.\n",
1201 sprintf( randstr
, "%d", secureRandom() );
1206 static int removeAuth
;
1208 static int removeSession
;
1209 static int removeCreds
;
1211 #ifdef HAVE_CKCONNECTOR
1212 static CkConnector
*ckConnector
;
1215 static GPipe ctlpipe
;
1216 static GTalk ctltalk
;
1219 sendStr( int lv
, const char *msg
)
1229 sendMsg( int lv, const char *msg, ... )
1234 va_start( args, msg );
1235 VASPrintf( &ae, msg, args );
1245 startClient( volatile int *pid
)
1247 const char *home
, *sessargs
, *desksess
;
1249 char **argv
, *fname
, *str
;
1251 char ** volatile pam_env
;
1252 # ifndef HAVE_PAM_GETENVLIST
1260 extern char **newenv
; /* from libs.a, this is set up by setpenv */
1263 #ifdef HAVE_CKCONNECTOR
1267 char *remoteHostName
= 0;
1268 const char *spaceStr
= "";
1270 char ckDeviceBuf
[20] = "";
1271 const char *ckDevice
= ckDeviceBuf
;
1272 dbus_bool_t isLocal
;
1274 char *failsafeArgv
[2];
1278 if (strCmp( dmrcuser
, curuser
)) {
1279 if (curdmrc
) { free( curdmrc
); curdmrc
= 0; }
1280 if (dmrcuser
) { free( dmrcuser
); dmrcuser
= 0; }
1283 #if defined(USE_PAM) || defined(_AIX)
1284 if (!(p
= getpwnam( curuser
))) {
1285 logError( "getpwnam(%s) failed.\n", curuser
);
1287 displayStr( V_MSG_ERR
, 0 );
1292 strcpy( curuser
, p
->pw_name
); /* Use normalized login name. */
1294 #ifdef HAVE_CKCONNECTOR
1295 if (!(ckConnector
= ck_connector_new())) {
1301 if (td
->serverVT
> 0)
1302 sprintf( ckDeviceBuf
, "/dev/tty%d", td
->serverVT
);
1304 isLocal
= ((td
->displayType
& d_location
) == dLocal
);
1311 family
= convertAddr( (char *)td
->peer
.data
, &length
, &data
);
1312 addr
.data
= (unsigned char *)data
;
1313 addr
.length
= length
;
1315 /* We are not simply using the addr part of td->name as it might be
1316 numeric due to the SourceAddress settings */
1317 remoteHostName
= networkAddressToHostname(family
, &addr
);
1321 dbus_error_init( &error
);
1322 ckStatus
= ck_connector_open_session_with_parameters( ckConnector
, &error
,
1323 "unix-user", &p
->pw_uid
,
1324 "x11-display-device", &ckDevice
,
1325 "x11-display", &td
->name
,
1326 "is-local", &isLocal
, /* meaning not entirely clear per doc */
1328 "remote-host-name", remoteHostName
?
1329 (const char **)&remoteHostName
: &spaceStr
,
1334 free( remoteHostName
);
1336 debug( "ck status: %d\n", ckStatus
);
1338 if (dbus_error_is_set( &error
)) {
1339 logError( "Cannot open ConsoleKit session: %s\n", error
.message
);
1340 dbus_error_free( &error
);
1342 logError( "Cannot open ConsoleKit session, likely OOM\n" );
1344 ck_connector_unref( ckConnector
);
1352 loginsuccess( curuser
, hostname
, tty
, &msg
);
1354 debug( "loginsuccess() - %s\n", msg
);
1355 free( (void *)msg
);
1358 # if defined(KERBEROS) && defined(AFS)
1359 if (krbtkfile
[0] != '\0') {
1362 if (k_setpag() == -1) {
1363 logError( "setpag() for %s failed\n", curuser
);
1366 if ((ret
= k_afsklog( NULL
, NULL
)) != KSUCCESS
) {
1367 logError( "AFS Warning: %s\n", krb_get_err_text( ret
) );
1371 displayMsg( V_MSG_ERR
,
1372 "Warning: Problems during Kerberos4/AFS setup." );
1375 # endif /* KERBEROS && AFS */
1382 env
= baseEnv( 0, curuser
);
1384 strApp( &xma
, "method=", curtype
, (char *)0 );
1386 strApp( &xma
, ",auto", (char *)0 );
1388 env
= setEnv( env
, "XDM_MANAGED", xma
);
1391 if (td
->autoLock
&& cursource
== PWSRC_AUTOLOGIN
)
1392 env
= setEnv( env
, "DESKTOP_LOCKED", "true" );
1393 env
= setEnv( env
, "PATH", curuid
? td
->userPath
: td
->systemPath
);
1394 env
= setEnv( env
, "SHELL", p
->pw_shell
);
1395 env
= setEnv( env
, "HOME", p
->pw_dir
);
1396 #if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS)
1397 if (krbtkfile
[0] != '\0')
1398 env
= setEnv( env
, "KRBTKFILE", krbtkfile
);
1400 #ifdef HAVE_CKCONNECTOR
1401 env
= setEnv( env
, "XDG_SESSION_COOKIE",
1402 ck_connector_get_cookie( ckConnector
) );
1404 userEnviron
= inheritEnv( env
, envvars
);
1405 env
= systemEnv( 0, curuser
);
1406 systemEnviron
= setEnv( env
, "HOME", p
->pw_dir
);
1407 debug( "user environment:\n%[|''>'\n's"
1408 "system environment:\n%[|''>'\n's"
1409 "end of environments\n",
1414 * for user-based authorization schemes,
1415 * add the user to the server's allowed "hosts" list.
1417 for (i
= 0; i
< td
->authNum
; i
++) {
1419 if (td
->authorizations
[i
]->name_length
== 9 &&
1420 !memcmp( td
->authorizations
[i
]->name
, "SUN-DES-1", 9 ))
1423 char netname
[MAXNETNAMELEN
+1];
1424 char domainname
[MAXNETNAMELEN
+1];
1426 getdomainname( domainname
, sizeof(domainname
) );
1427 user2netname( netname
, curuid
, domainname
);
1428 addr
.family
= FamilyNetname
;
1429 addr
.length
= strlen( netname
);
1430 addr
.address
= netname
;
1431 XAddHost( dpy
, &addr
);
1435 if (td
->authorizations
[i
]->name_length
== 14 &&
1436 !memcmp( td
->authorizations
[i
]->name
, "MIT-KERBEROS-5", 14 ))
1438 /* Update server's auth file with user-specific info.
1439 * Don't need to AddHost because X server will do that
1440 * automatically when it reads the cache we are about
1443 XauDisposeAuth( td
->authorizations
[i
] );
1444 td
->authorizations
[i
] =
1445 krb5GetAuthFor( 14, "MIT-KERBEROS-5", td
->name
);
1446 saveServerAuthorizations( td
, td
->authorizations
, td
->authNum
);
1452 mergeSessionArgs( True
);
1454 debug( "now starting the session\n" );
1458 # ifdef HAVE_SETUSERCONTEXT
1459 if (setusercontext( lc
, p
, p
->pw_uid
, LOGIN_SETGROUP
)) {
1460 logError( "setusercontext(groups) for %s failed: %m\n",
1465 if (!setGid( curuser
, curgid
))
1469 # ifndef HAVE_PAM_GETENVLIST
1470 if (!(pam_env
= initStrArr( 0 ))) {
1474 saved_env
= environ
;
1477 removeCreds
= True
; /* set it first - i don't trust PAM's rollback */
1478 pretc
= pam_setcred( pamh
, 0 );
1480 # ifndef HAVE_PAM_GETENVLIST
1482 environ
= saved_env
;
1484 # ifdef HAVE_INITGROUPS
1485 /* This seems to be a strange place for it, but do it:
1486 - after the initial groups are set
1487 - after pam_setcred might have set something, even in the error case
1488 - before pam_setcred(DELETE_CRED) might need it
1493 if (pretc
!= PAM_SUCCESS
) {
1494 logError( "pam_setcred() for %s failed: %s\n",
1495 curuser
, pam_strerror( pamh
, pretc
) );
1500 removeSession
= True
; /* set it first - same as above */
1501 pretc
= pam_open_session( pamh
, 0 );
1503 if (pretc
!= PAM_SUCCESS
) {
1504 logError( "pam_open_session() for %s failed: %s\n",
1505 curuser
, pam_strerror( pamh
, pretc
) );
1510 /* we don't want sessreg and the startup/reset scripts run with user
1511 credentials. unfortunately, we can reset only the gids. */
1514 # define D_LOGIN_SETGROUP LOGIN_SETGROUP
1516 # define D_LOGIN_SETGROUP 0
1517 #endif /* USE_PAM */
1520 chownCtrl( &td
->ctrl
, curuid
);
1521 ctltalk
.pipe
= &ctlpipe
;
1522 ASPrintf( &buf
, "sub-daemon for display %s", td
->name
);
1523 ASPrintf( &buf2
, "client for display %s", td
->name
);
1524 switch (gFork( &ctlpipe
, buf
, buf2
, 0, 0, mstrtalk
.pipe
, pid
)) {
1527 gCloseOnExec( ctltalk
.pipe
);
1528 if (Setjmp( ctltalk
.errjmp
))
1531 gCloseOnExec( mstrtalk
.pipe
);
1532 if (Setjmp( mstrtalk
.errjmp
))
1536 setproctitle( "%s'", td
->name
);
1538 strApp( &prog
, " '", (char *)0 );
1542 Signal( SIGINT
, SIG_DFL
);
1544 sessreg( td
, getpid(), curuser
, curuid
);
1546 /* We do this here, as we want to have the session as parent. */
1547 switch (source( systemEnviron
, td
->startup
, td_setup
)) {
1550 case wcCompose( 0, 0, 127 ):
1552 default: /* Explicit failure => message already displayed. */
1553 logError( "Startup script returned non-zero exit code\n" );
1557 /* Memory leaks are ok here as we exec() soon. */
1559 #if defined(USE_PAM) || !defined(_AIX)
1562 /* pass in environment variables set by libpam and modules it called */
1563 # ifdef HAVE_PAM_GETENVLIST
1564 pam_env
= pam_getenvlist( pamh
);
1568 for (; *pam_env
; pam_env
++)
1569 userEnviron
= putEnv( *pam_env
, userEnviron
);
1572 # ifdef HAVE_SETLOGIN
1573 if (setlogin( curuser
) < 0) {
1574 logError( "setlogin for %s failed: %m\n", curuser
);
1577 # define D_LOGIN_SETLOGIN LOGIN_SETLOGIN
1579 # define D_LOGIN_SETLOGIN 0
1582 # if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1587 # ifndef HAVE_SETUSERCONTEXT
1590 if (!setUid( curuser
, curuid
))
1593 if (!setUser( curuser
, curuid
, curgid
))
1597 # else /* !HAVE_SETUSERCONTEXT */
1600 * Destroy environment.
1601 * We need to do this before setusercontext() because that may
1602 * set or reset some environment variables.
1604 if (!(environ
= initStrArr( 0 )))
1608 * Set the user's credentials: uid, gid, groups,
1609 * environment variables, resource limits, and umask.
1611 if (setusercontext( lc
, p
, p
->pw_uid
,
1612 LOGIN_SETALL
& ~(D_LOGIN_SETGROUP
|D_LOGIN_SETLOGIN
) ) < 0)
1614 logError( "setusercontext for %s failed: %m\n", curuser
);
1618 for (i
= 0; environ
[i
]; i
++)
1619 userEnviron
= putEnv( environ
[i
], userEnviron
);
1621 # endif /* !HAVE_SETUSERCONTEXT */
1623 #else /* PAM || !_AIX */
1625 * Set the user's credentials: uid, gid, groups,
1626 * audit classes, user limits, and umask.
1628 if (setpcred( curuser
, NULL
) == -1) {
1629 logError( "setpcred for %s failed: %m\n", curuser
);
1634 * Set the users process environment. Store protected variables and
1635 * obtain updated user environment list. This call will initialize
1638 userEnviron
= xCopyStrArr( 1, userEnviron
);
1639 userEnviron
[0] = (char *)"USRENVIRON:";
1640 if (setpenv( curuser
, PENV_INIT
| PENV_ARGV
| PENV_NOEXEC
,
1641 userEnviron
, NULL
) != 0)
1643 logError( "Cannot set %s's process environment\n", curuser
);
1646 userEnviron
= newenv
;
1651 * for user-based authorization schemes,
1652 * use the password to get the user's credentials.
1655 /* do like "keylogin" program */
1657 logInfo( "No password for NIS provided.\n" );
1659 char netname
[MAXNETNAMELEN
+1], secretkey
[HEXKEYBYTES
+1];
1660 int nameret
, keyret
;
1662 int key_set_ok
= False
;
1663 struct key_netstarg netst
;
1665 nameret
= getnetname( netname
);
1666 debug( "user netname: %s\n", netname
);
1667 len
= strlen( curpass
);
1669 bzero( curpass
+ 8, len
- 8 );
1670 keyret
= getsecretkey( netname
, secretkey
, curpass
);
1671 debug( "getsecretkey returns %d, key length %d\n",
1672 keyret
, strlen( secretkey
) );
1673 netst
.st_netname
= netname
;
1674 memcpy( netst
.st_priv_key
, secretkey
, HEXKEYBYTES
);
1675 memset( netst
.st_pub_key
, 0, HEXKEYBYTES
);
1676 if (key_setnet( &netst
) < 0)
1677 debug( "Could not set secret key.\n" );
1678 /* is there a key, and do we have the right password? */
1681 keyret
= key_setsecret( secretkey
);
1682 debug( "key_setsecret returns %d\n", keyret
);
1684 logError( "Failed to set NIS secret key\n" );
1688 /* found a key, but couldn't interpret it */
1689 logError( "Password incorrect for NIS principal %s\n",
1690 nameret
? netname
: curuser
);
1694 nukeAuth( 9, "SUN-DES-1" );
1695 bzero( secretkey
, strlen( secretkey
) );
1699 /* do like "kinit" program */
1701 logInfo( "No password for Kerberos5 provided.\n" );
1703 if ((str
= krb5Init( curuser
, curpass
, td
->name
)))
1704 userEnviron
= setEnv( userEnviron
, "KRB5CCNAME", str
);
1706 nukeAuth( 14, "MIT-KERBEROS-5" );
1708 if (td
->autoReLogin
) {
1710 gSendInt( D_ReLogin
);
1711 gSendStr( curuser
);
1712 gSendStr( curpass
);
1713 gSendStr( newdmrc
);
1716 bzero( curpass
, strlen( curpass
) );
1717 setUserAuthorization( td
);
1718 home
= getEnv( userEnviron
, "HOME" );
1719 if (home
&& chdir( home
) < 0) {
1720 logError( "Cannot chdir to %s's home %s: %m\n", curuser
, home
);
1721 sendStr( V_MSG_ERR
, "Cannot enter home directory. Using /.\n" );
1723 userEnviron
= setEnv( userEnviron
, "HOME", "/" );
1726 if (home
|| td
->clientLogFile
[0] == '/') {
1727 if (!createClientLog( td
->clientLogFile
)) {
1728 logWarn( "Session log file according to %s cannot be created: %m\n",
1729 td
->clientLogFile
);
1734 if (!createClientLog( td
->clientLogFallback
))
1735 logError( "Fallback session log file according to %s cannot be created: %m\n",
1736 td
->clientLogFallback
);
1737 /* Could inform the user, but I guess this is only confusing. */
1740 mergeSessionArgs( home
!= 0 );
1741 if (!(desksess
= iniEntry( curdmrc
, "Desktop", "Session", 0 )))
1742 desksess
= "failsafe"; /* only due to OOM */
1746 gSendStr( curuser
);
1747 gSendStr( desksess
);
1748 close( mstrtalk
.pipe
->fd
.w
);
1749 userEnviron
= setEnv( userEnviron
, "DESKTOP_SESSION", desksess
);
1750 for (i
= 0; td
->sessionsDirs
[i
]; i
++) {
1752 if (strApp( &fname
, td
->sessionsDirs
[i
], "/", desksess
, ".desktop", (char *)0 )) {
1753 if ((str
= iniLoad( fname
))) {
1754 if (!strCmp( iniEntry( str
, "Desktop Entry", "Hidden", 0 ), "true" ) ||
1755 !(sessargs
= iniEntry( str
, "Desktop Entry", "Exec", 0 )))
1764 if (!strcmp( desksess
, "failsafe" ) ||
1765 !strcmp( desksess
, "default" ) ||
1766 !strcmp( desksess
, "custom" ))
1767 sessargs
= desksess
;
1771 if (!(argv
= parseArgs( (char **)0, td
->session
)) ||
1772 !(argv
= addStrArr( argv
, sessargs
, -1 )))
1774 if (argv
[0] && *argv
[0]) {
1775 debug( "executing session %\"[s\n", argv
);
1776 execute( argv
, userEnviron
);
1777 logError( "Session %\"s execution failed: %m\n", argv
[0] );
1779 logError( "Session has no command/arguments\n" );
1780 failsafeArgv
[0] = td
->failsafeClient
;
1781 failsafeArgv
[1] = 0;
1782 execute( failsafeArgv
, userEnviron
);
1783 logError( "Failsafe client %\"s execution failed: %m\n",
1786 sendStr( V_MSG_ERR
, 0 );
1792 debug( "StartSession, fork succeeded %d\n", *pid
);
1799 if (!Setjmp( ctltalk
.errjmp
))
1800 while (gRecvCmd( &i
)) {
1802 displayStr( i
, buf
);
1807 gClosen( ctltalk
.pipe
);
1814 clientExited( void )
1820 #ifdef HAVE_CKCONNECTOR
1825 switch (source( systemEnviron
, td
->reset
, td_setup
)) {
1827 case wcCompose( 0, 0, 127 ):
1830 logError( "Reset script returned non-zero exit code\n" );
1833 sessreg( td
, 0, 0, 0 );
1835 switch (Fork( &pid
)) {
1837 #if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1838 if (restoreGids() && setUid( curuser
, curuid
))
1840 if (setUser( curuser
, curuid
, curgid
))
1844 removeUserAuthorization( td
);
1846 krb5Destroy( td
->name
);
1848 #if !defined(USE_PAM) && !defined(_AIX)
1858 #endif /* !USE_PAM && !_AIX*/
1862 logError( "Cannot clean up session: fork() failed: %m" );
1873 # ifdef HAVE_INITGROUPS
1876 if (removeSession
) {
1877 pretc
= pam_close_session( pamh
, 0 );
1879 if (pretc
!= PAM_SUCCESS
)
1880 logError( "pam_close_session() failed: %s\n",
1881 pam_strerror( pamh
, pretc
) );
1883 pretc
= pam_setcred( pamh
, PAM_DELETE_CRED
);
1885 if (pretc
!= PAM_SUCCESS
)
1886 logError( "pam_setcred(DELETE_CRED) failed: %s\n",
1887 pam_strerror( pamh
, pretc
) );
1889 removeCreds
= False
;
1892 pam_end( pamh
, PAM_SUCCESS
);
1898 #ifdef HAVE_CKCONNECTOR
1900 dbus_error_init( &error
);
1901 if (!ck_connector_close_session( ckConnector
, &error
)) {
1902 if (dbus_error_is_set( &error
)) {
1903 logError( "Cannot close ConsoleKit session: %s\n", error
.message
);
1904 dbus_error_free( &error
);
1906 logError( "Cannot close ConsoleKit session, likely OOM\n" );
1909 ck_connector_unref( ckConnector
);
1916 sessionExit( int status
)
1918 Signal( SIGTERM
, SIG_IGN
);
1924 /* make sure the server gets reset after the session is over */
1925 if (td
->serverPid
>= 2) {
1926 if (!td
->terminateServer
&& td
->resetSignal
)
1927 terminateProcess( td
->serverPid
, td
->resetSignal
);
1930 debug( "display %s exiting with status %d\n", td
->name
, status
);
1937 char *data
, *fname
= 0;
1938 int len
, pid
, pfd
[2], err
;
1941 if (!dmrcuser
|| !dmrcuser
[0] || !(p
= getpwnam( dmrcuser
)))
1945 if (!strApp( &fname
, dmrcDir
, "/", p
->pw_name
, ".dmrc", (char *)0 ))
1947 if (!(curdmrc
= iniLoad( fname
))) {
1955 if (!strApp( &fname
, p
->pw_dir
, "/.dmrc", (char *)0 ))
1959 switch (Fork( &pid
)) {
1965 if (!setUser( p
->pw_name
, p
->pw_uid
, p
->pw_gid
))
1967 if (!(data
= iniLoad( fname
))) {
1968 static const int m1
= -1;
1969 write( pfd
[1], &m1
, sizeof(int) );
1972 len
= strlen( data
);
1973 write( pfd
[1], &len
, sizeof(int) );
1974 write( pfd
[1], data
, len
+ 1 );
1980 if (reader( pfd
[0], &len
, sizeof(int) ) == sizeof(int)) {
1983 else if ((curdmrc
= Malloc( len
+ 1 ))) {
1984 if (reader( pfd
[0], curdmrc
, len
+ 1 ) == len
+ 1)