3
Copyright 1988, 1998 The Open Group
4
Copyright 2000-2004 Oswald Buddenhagen <ossi@kde.org>
6
Permission to use, copy, modify, distribute, and sell this software and its
7
documentation for any purpose is hereby granted without fee, provided that
8
the above copyright notice appear in all copies and that both that
9
copyright notice and this permission notice appear in supporting
12
The above copyright notice and this permission notice shall be included
13
in all copies or substantial portions of the Software.
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16
OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
18
IN NO EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR
19
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
20
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
21
OTHER DEALINGS IN THE SOFTWARE.
23
Except as contained in this notice, the name of a copyright holder shall
24
not be used in advertising or otherwise to promote the sale, use or
25
other dealings in this Software without prior written authorization
26
from the copyright holder.
31
* xdm - display manager daemon
32
* Author: Keith Packard, MIT X Consortium
34
* user verification and session initiation.
45
# include <rpc/key_prot.h>
46
extern int key_setnet( struct key_netstarg *arg );
49
# include <krb5/krb5.h>
51
#ifdef HAVE_SETUSERCONTEXT
52
# include <login_cap.h>
55
# ifdef HAVE_PAM_PAM_APPL_H
56
# include <pam/pam_appl.h>
58
# include <security/pam_appl.h>
60
#elif defined(_AIX) /* USE_PAM */
63
extern int loginrestrictions( const char *Name, const int Mode, const char *Tty, char **Msg );
64
extern int loginfailed( const char *User, const char *Host, const char *Tty );
65
extern int loginsuccess( const char *User, const char *Host, const char *Tty, char **Msg );
66
#else /* USE_PAM || _AIX */
71
# include <sys/param.h>
78
# include <sys/types.h>
82
#endif /* USE_PAM || _AIX */
85
* Session data, mostly what struct verify_info was for
101
static struct passwd *p;
102
#ifdef HAVE_SETUSERCONTEXT
103
# ifdef HAVE_LOGIN_GETCLASS
106
struct login_cap *lc;
110
static pam_handle_t *pamh;
112
static char tty[16], hostname[100];
115
static struct spwd *sp;
118
static char krbtkfile[MAXPATHLEN];
123
displayStr( int lv, const char *msg )
131
#if !defined(USE_PAM) && (defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW))
133
displayMsg( int lv, const char *msg, ... )
138
va_start( args, msg );
139
VASPrintf( &ae, msg, args );
142
displayStr( lv, ae );
150
prepareErrorGreet(); \
151
gSendInt( V_AUTH ); \
155
#define V_RET_FAIL(m) \
157
displayStr( V_MSG_ERR, m ); \
158
gSendInt( V_FAIL ); \
164
# ifndef PAM_MESSAGE_CONST
165
typedef struct pam_message pam_message_type;
166
typedef void *pam_gi_type;
168
typedef const struct pam_message pam_message_type;
169
typedef const void *pam_gi_type;
179
PAM_conv( int num_msg,
180
pam_message_type **msg,
181
struct pam_response **resp,
185
struct pam_response *reply;
186
struct pam_data *pd = (struct pam_data *)appdata_ptr;
188
if (!(reply = Calloc( num_msg, sizeof(*reply) )))
192
debug( "PAM_conv\n" );
193
for (count = 0; count < num_msg; count++)
194
switch (msg[count]->msg_style) {
196
debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg );
197
displayStr( V_MSG_INFO, msg[count]->msg );
200
debug( " PAM_ERROR_MSG: %s\n", msg[count]->msg );
201
displayStr( V_MSG_ERR, msg[count]->msg );
204
/* could do better error handling here, but see below ... */
206
switch (msg[count]->msg_style) {
207
/* case PAM_PROMPT_ECHO_ON: cannot happen */
208
case PAM_PROMPT_ECHO_OFF:
209
debug( " PAM_PROMPT_ECHO_OFF (usecur): %s\n", msg[count]->msg );
211
pd->gconv( GCONV_PASS, 0 );
212
strDup( &reply[count].resp, curpass );
215
logError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
219
switch (msg[count]->msg_style) {
220
case PAM_PROMPT_ECHO_ON:
221
debug( " PAM_PROMPT_ECHO_ON: %s\n", msg[count]->msg );
222
reply[count].resp = pd->gconv( GCONV_NORMAL, msg[count]->msg );
224
case PAM_PROMPT_ECHO_OFF:
225
debug( " PAM_PROMPT_ECHO_OFF: %s\n", msg[count]->msg );
226
reply[count].resp = pd->gconv( GCONV_HIDDEN, msg[count]->msg );
228
#ifdef PAM_BINARY_PROMPT
229
case PAM_BINARY_PROMPT:
230
debug( " PAM_BINARY_PROMPT\n" );
231
reply[count].resp = pd->gconv( GCONV_BINARY, msg[count]->msg );
235
logError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
239
if (!reply[count].resp) {
240
debug( " PAM_conv aborted\n" );
244
reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
246
debug( " PAM_conv success\n" );
251
for (; count >= 0; count--)
252
if (reply[count].resp)
253
switch (msg[count]->msg_style) {
254
case PAM_PROMPT_ECHO_ON:
255
case PAM_PROMPT_ECHO_OFF: /* could wipe ... */
256
#ifdef PAM_BINARY_PROMPT
257
case PAM_BINARY_PROMPT: /* ... that too ... */
259
free( reply[count].resp );
266
# ifdef PAM_FAIL_DELAY
268
fail_delay( int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED,
269
void *appdata_ptr ATTR_UNUSED )
274
doPAMAuth( const char *psrv, struct pam_data *pdata )
277
struct pam_conv pconv;
280
pdata->abort = FALSE;
281
pconv.conv = PAM_conv;
282
pconv.appdata_ptr = (void *)pdata;
283
debug( " PAM service %s\n", psrv );
284
if ((pretc = pam_start( psrv, curuser, &pconv, &pamh )) != PAM_SUCCESS)
286
if ((pretc = pam_set_item( pamh, PAM_TTY, td->name )) != PAM_SUCCESS) {
288
pam_end( pamh, pretc );
292
logError( "PAM error: %s\n", pam_strerror( 0, pretc ) );
295
if ((td->displayType & d_location) == dForeign) {
296
char *cp = strchr( td->name, ':' );
298
pretc = pam_set_item( pamh, PAM_RHOST, td->name );
300
if (pretc != PAM_SUCCESS)
303
# ifdef __sun__ /* Only Solaris <= 9, but checking it does not seem worth it. */
304
else if (pam_set_item( pamh, PAM_RHOST, 0 ) != PAM_SUCCESS)
307
# ifdef PAM_FAIL_DELAY
308
pam_set_item( pamh, PAM_FAIL_DELAY, (void *)fail_delay );
312
debug( " pam_authenticate() ...\n" );
313
pretc = pam_authenticate( pamh,
314
td->allowNullPasswd ? 0 : PAM_DISALLOW_NULL_AUTHTOK );
316
debug( " pam_authenticate() returned: %s\n", pam_strerror( pamh, pretc ) );
318
pam_end( pamh, PAM_SUCCESS );
323
debug( " asking PAM for user ...\n" );
324
pam_get_item( pamh, PAM_USER, &pitem );
326
strDup( &curuser, (const char *)pitem );
327
gSendInt( V_PUT_USER );
330
if (pretc != PAM_SUCCESS) {
332
case PAM_USER_UNKNOWN:
334
case PAM_MAXTRIES: /* should handle this better ... */
335
case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */
336
pam_end( pamh, pretc );
340
pam_end( pamh, pretc );
351
#if defined(USE_PAM) || defined(_AIX)
352
isNoPassAllowed( const char *un )
354
struct passwd *pw = 0;
356
isNoPassAllowed( const char *un, struct passwd *pw )
366
if (cursource != PWSRC_MANUAL)
369
for (hg = 0, fp = td->noPassUsers; *fp; fp++)
372
else if (!strcmp( un, *fp ))
374
else if (!strcmp( "*", *fp )) {
375
#if defined(USE_PAM) || defined(_AIX)
376
if (!(pw = getpwnam( un )))
383
#if defined(USE_PAM) || defined(_AIX)
384
if (hg && (pw || (pw = getpwnam( un )))) {
388
for (setgrent(); (gr = getgrent()); )
389
for (fp = td->noPassUsers; *fp; fp++)
390
if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) {
391
if (pw->pw_gid == gr->gr_gid) {
395
for (; *gr->gr_mem; gr->gr_mem++)
396
if (!strcmp( un, *gr->gr_mem )) {
407
#if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT)
408
# define LC_RET0 do { login_close(lc); return 0; } while(0)
410
# define LC_RET0 return 0
414
verify( GConvFunc gconv, int rootok )
418
struct pam_data pdata;
429
# ifdef HAVE_GETUSERSHELL
432
# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
433
int tim, expir, warntime, quietlog;
437
debug( "verify ...\n" );
442
if (!strcmp( curtype, "classic" )) {
443
if (!gconv( GCONV_USER, 0 ))
445
if (isNoPassAllowed( curuser )) {
446
gconv( GCONV_PASS_ND, 0 );
449
sprintf( psrvb, "%.31s-np", PAMService );
457
sprintf( psrvb, "%.31s-%.31s", PAMService, curtype );
459
pdata.usecur = FALSE;
462
if (!doPAMAuth( psrv, &pdata ))
467
if ((td->displayType & d_location) == dForeign) {
469
strncpy( hostname, td->name, sizeof(hostname) - 1 );
470
hostname[sizeof(hostname)-1] = '\0';
471
if ((tmpch = strchr( hostname, ':' )))
476
/* tty names should only be 15 characters long */
478
for (i = 0; i < 15 && td->name[i]; i++) {
479
if (td->name[i] == ':' || td->name[i] == '.')
482
tty[i] = td->name[i];
486
memcpy( tty, "/dev/xdm/", 9 );
487
for (i = 0; i < 6 && td->name[i]; i++) {
488
if (td->name[i] == ':' || td->name[i] == '.')
491
tty[9 + i] = td->name[i];
496
if (!strcmp( curtype, "classic" )) {
497
if (!gconv( GCONV_USER, 0 ))
499
if (isNoPassAllowed( curuser )) {
500
gconv( GCONV_PASS_ND, 0 );
502
debug( "accepting despite empty password\n" );
506
if (!gconv( GCONV_PASS, 0 ))
510
if ((i = authenticate( curuser, curpass, &reenter, &msg ))) {
511
debug( "authenticate() failed: %s\n", msg );
514
loginfailed( curuser, hostname, tty );
515
if (i == ENOENT || i == ESAD)
521
logError( "authenticate() requests more data: %s\n", msg );
525
} else if (!strcmp( curtype, "generic" )) {
526
if (!gconv( GCONV_USER, 0 ))
530
if ((i = authenticate( curuser, curret, &reenter, &msg ))) {
531
debug( "authenticate() failed: %s\n", msg );
534
loginfailed( curuser, hostname, tty );
535
if (i == ENOENT || i == ESAD)
544
if (!(curret = gconv( GCONV_HIDDEN, msg )))
549
logError( "Unsupported authentication type %\"s requested\n", curtype );
553
displayStr( V_MSG_INFO, msg );
561
if (strcmp( curtype, "classic" )) {
562
logError( "Unsupported authentication type %\"s requested\n", curtype );
566
if (!gconv( GCONV_USER, 0 ))
569
if (!(p = getpwnam( curuser ))) {
570
debug( "getpwnam() failed.\n" );
573
# ifdef __linux__ /* only Linux? */
574
if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
575
debug( "account is locked\n" );
581
if ((sp = getspnam( curuser )))
582
p->pw_passwd = sp->sp_pwdp;
584
debug( "getspnam() failed: %m. Are you root?\n" );
587
if (!*p->pw_passwd) {
588
if (!td->allowNullPasswd) {
589
debug( "denying user with empty password\n" );
595
if (isNoPassAllowed( curuser, p )) {
597
gconv( GCONV_PASS_ND, 0 );
599
debug( "accepting password-less login\n" );
603
if (!gconv( GCONV_PASS, 0 ))
609
char realm[REALM_SZ];
611
if (krb_get_lrealm( realm, 1 )) {
612
logError( "Can't get KerberosIV realm.\n" );
616
sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name );
617
krb_set_tkt_string( krbtkfile );
620
ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" );
621
if (ret == KSUCCESS) {
622
chown( krbtkfile, p->pw_uid, p->pw_gid );
623
debug( "KerberosIV verify succeeded\n" );
625
} else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) {
626
logError( "KerberosIV verification failure %\"s for %s\n",
627
krb_get_err_text( ret ), curuser );
631
debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) );
634
# endif /* KERBEROS */
636
# if defined(ultrix) || defined(__ultrix__)
637
if (authenticate_user( p, curpass, NULL ) < 0)
638
# elif defined(HAVE_CRYPT)
639
if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd ))
641
if (strcmp( curpass, p->pw_passwd ))
644
debug( "password verify failed\n" );
650
#endif /* !defined(USE_PAM) && !defined(_AIX) */
652
debug( "restrict %s ...\n", curuser );
654
#if defined(USE_PAM) || defined(_AIX)
655
if (!(p = getpwnam( curuser ))) {
656
logError( "getpwnam(%s) failed.\n", curuser );
661
if (!rootok && !td->allowRootLogin)
662
V_RET_FAIL( "Root logins are not allowed" );
663
return 1; /* don't deny root to log in */
668
debug( " pam_acct_mgmt() ...\n" );
669
pretc = pam_acct_mgmt( pamh, 0 );
671
debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) );
672
if (pretc == PAM_NEW_AUTHTOK_REQD) {
673
pdata.usecur = FALSE;
674
pdata.gconv = conv_interact;
675
/* pam will have output a message already, so no prepareErrorGreet() */
676
if (gconv != conv_interact || pnopass) {
677
pam_end( pamh, PAM_SUCCESS );
679
gSendInt( V_CHTOK_AUTH );
680
/* this cannot auth the wrong user, as only classic auths get here */
681
while (!doPAMAuth( PAMService, &pdata ))
684
gSendInt( V_PRE_OK );
688
debug( " pam_chauthtok() ...\n" );
689
pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK );
691
debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) );
693
pam_end( pamh, PAM_SUCCESS );
697
if (pretc == PAM_SUCCESS)
699
/* effectively there is only PAM_AUTHTOK_ERR */
706
} else if (pretc != PAM_SUCCESS) {
707
pam_end( pamh, pretc );
712
#elif defined(_AIX) /* USE_PAM */
715
if (loginrestrictions( curuser,
716
((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN,
719
debug( "loginrestrictions() - %s\n", msg ? msg : "error" );
720
loginfailed( curuser, hostname, tty );
723
displayStr( V_MSG_ERR, msg );
732
#endif /* USE_PAM || _AIX */
736
# ifdef HAVE_SETUSERCONTEXT
737
# ifdef HAVE_LOGIN_GETCLASS
738
lc = login_getclass( p->pw_class );
740
lc = login_getpwclass( p );
745
p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell );
750
/* restrict_expired */
751
# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
753
# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW))
758
# define DEFAULT_WARN (2L * 7L) /* Two weeks */
760
tim = time( NULL ) / 86400L;
762
# ifdef HAVE_SETUSERCONTEXT
763
quietlog = login_getcapbool( lc, "hushlogin", 0 );
764
warntime = login_getcaptime( lc, "warnexpire",
765
DEFAULT_WARN * 86400L,
766
DEFAULT_WARN * 86400L ) / 86400L;
770
warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN;
772
warntime = DEFAULT_WARN;
776
# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
778
expir = p->pw_expire / 86400L;
780
if (sp->sp_expire != -1) {
781
expir = sp->sp_expire;
784
displayStr( V_MSG_ERR,
785
"Your account has expired;"
786
" please contact your system administrator" );
789
} else if (tim > (expir - warntime) && !quietlog) {
790
displayMsg( V_MSG_INFO,
791
"Warning: your account will expire in %d day(s)",
796
# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
798
expir = p->pw_change / 86400L;
800
if (!sp->sp_lstchg) {
801
displayStr( V_MSG_ERR,
802
"You are required to change your password immediately"
803
" (root enforced)" );
804
/* XXX todo password change */
807
} else if (sp->sp_max != -1) {
808
expir = sp->sp_lstchg + sp->sp_max;
809
if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) {
810
displayStr( V_MSG_ERR,
811
"Your account has expired;"
812
" please contact your system administrator" );
818
displayStr( V_MSG_ERR,
819
"You are required to change your password immediately"
820
" (password aged)" );
821
/* XXX todo password change */
824
} else if (tim > (expir - warntime) && !quietlog) {
825
displayMsg( V_MSG_INFO,
826
"Warning: your password will expire in %d day(s)",
833
# endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */
835
/* restrict_nologin */
836
# ifndef _PATH_NOLOGIN
837
# define _PATH_NOLOGIN "/etc/nologin"
841
# ifdef HAVE_SETUSERCONTEXT
842
/* Do we ignore a nologin file? */
843
!login_getcapbool( lc, "ignorenologin", 0 )) &&
844
(!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) ||
846
!stat( (nolg = _PATH_NOLOGIN), &st )))
848
if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) {
849
if ((buf = Malloc( st.st_size + 1 ))) {
850
if (read( fd, buf, st.st_size ) == st.st_size) {
853
displayStr( V_MSG_ERR, buf );
862
displayStr( V_MSG_ERR,
863
"Logins are not allowed at the moment.\nTry again later" );
869
# if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK)
870
if (!auth_timeok( lc, time( NULL ) )) {
871
displayStr( V_MSG_ERR,
872
"You are not allowed to login at the moment" );
878
# ifdef HAVE_GETUSERSHELL
880
if (!(s = getusershell())) {
881
debug( "shell not in /etc/shells\n" );
883
V_RET_FAIL( "Your login shell is not listed in /etc/shells" );
885
if (!strcmp( s, p->pw_shell )) {
892
# endif /* !USE_PAM */
894
/* restrict_nohome */
895
# ifdef HAVE_SETUSERCONTEXT
896
if (login_getcapbool( lc, "requirehome", 0 )) {
898
if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) {
899
displayStr( V_MSG_ERR, "Home folder not available" );
913
static const char *envvars[] = {
914
"TZ", /* SYSV and SVR4, but never hurts */
916
"AUTHSTATE", /* for kerberos */
922
#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
923
static int num_saved_gids;
924
static gid_t *saved_gids;
929
num_saved_gids = getgroups( 0, 0 );
930
if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids )))
932
if (getgroups( num_saved_gids, saved_gids ) < 0) {
933
logError( "saving groups failed: %m\n" );
942
if (setgroups( num_saved_gids, saved_gids ) < 0) {
943
logError( "restoring groups failed: %m\n" );
946
if (setgid( p->pw_gid ) < 0) {
947
logError( "restoring gid failed: %m\n" );
952
#endif /* USE_PAM && HAVE_INITGROUPS */
957
#ifdef HAVE_INITGROUPS
958
if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) {
959
logError( "restoring groups failed: %m\n" );
963
if (setgid( 0 ) < 0) {
964
logError( "restoring gid failed: %m\n" );
971
setGid( const char *name, int gid )
973
if (setgid( gid ) < 0) {
974
logError( "setgid(%d) (user %s) failed: %m\n", gid, name );
977
#ifdef HAVE_INITGROUPS
978
if (initgroups( name, gid ) < 0) {
979
logError( "initgroups for %s failed: %m\n", name );
983
#endif /* QNX4 doesn't support multi-groups, no initgroups() */
988
setUid( const char *name, int uid )
990
if (setuid( uid ) < 0) {
991
logError( "setuid(%d) (user %s) failed: %m\n", uid, name );
998
setUser( const char *name, int uid, int gid )
1000
if (setGid( name, gid )) {
1001
if (setUid( name, uid ))
1008
#if defined(SECURE_RPC) || defined(K5AUTH)
1010
nukeAuth( int len, const char *name )
1014
for (i = 0; i < td->authNum; i++)
1015
if (td->authorizations[i]->name_length == len &&
1016
!memcmp( td->authorizations[i]->name, name, len ))
1018
memcpy( &td->authorizations[i], &td->authorizations[i+1],
1019
sizeof(td->authorizations[i]) * (--td->authNum - i) );
1026
mergeSessionArgs( int cansave )
1034
if ((!curdmrc || newdmrc) && *dmrcDir)
1035
if (strApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 ))
1039
curdmrc = iniLoad( fname );
1041
strDup( &curdmrc, "[Desktop]\nSession=default\n" );
1046
curdmrc = iniMerge( curdmrc, newdmrc );
1049
if (needsave && cansave)
1050
if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) {
1051
for (i = 0; mfname[i]; i++)
1052
if (mfname[i] == '/') {
1054
mkdir( mfname, 0755 );
1057
iniSave( curdmrc, mfname );
1064
createClientLog( const char *log )
1066
char randstr[32], *randstrp = 0, *lname;
1070
struct expando macros[] = {
1071
{ 'd', 0, td->name },
1072
{ 'u', 0, curuser },
1073
{ 'r', 0, randstrp },
1076
if (!(lname = expandMacros( log, macros )))
1079
if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) >= 0) {
1086
if (errno != EEXIST || !macros[2].uses) {
1090
logInfo( "Session log file %s not usable, trying another one.\n",
1093
sprintf( randstr, "%d", secureRandom() );
1098
static int removeAuth;
1100
static int removeSession;
1101
static int removeCreds;
1104
static GPipe ctlpipe;
1105
static GTalk ctltalk;
1108
sendStr( int lv, const char *msg )
1118
sendMsg( int lv, const char *msg, ... )
1123
va_start( args, msg );
1124
VASPrintf( &ae, msg, args );
1134
startClient( volatile int *pid )
1136
const char *home, *sessargs, *desksess;
1138
char **argv, *fname, *str;
1140
char ** volatile pam_env;
1141
# ifndef HAVE_PAM_GETENVLIST
1149
extern char **newenv; /* from libs.a, this is set up by setpenv */
1152
#ifdef HAVE_SETUSERCONTEXT
1153
extern char **environ;
1155
char *failsafeArgv[2];
1159
if (strCmp( dmrcuser, curuser )) {
1160
if (curdmrc) { free( curdmrc ); curdmrc = 0; }
1161
if (dmrcuser) { free( dmrcuser ); dmrcuser = 0; }
1164
#if defined(USE_PAM) || defined(_AIX)
1165
if (!(p = getpwnam( curuser ))) {
1166
logError( "getpwnam(%s) failed.\n", curuser );
1168
displayStr( V_MSG_ERR, 0 );
1176
loginsuccess( curuser, hostname, tty, &msg );
1178
debug( "loginsuccess() - %s\n", msg );
1179
free( (void *)msg );
1182
# if defined(KERBEROS) && defined(AFS)
1183
if (krbtkfile[0] != '\0') {
1186
if (k_setpag() == -1) {
1187
logError( "setpag() for %s failed\n", curuser );
1190
if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS) {
1191
logError( "AFS Warning: %s\n", krb_get_err_text( ret ) );
1195
displayMsg( V_MSG_ERR,
1196
"Warning: Problems during Kerberos4/AFS setup." );
1199
# endif /* KERBEROS && AFS */
1206
env = baseEnv( curuser );
1208
strApp( &xma, "method=", curtype, (char *)0 );
1210
strApp( &xma, ",auto", (char *)0 );
1212
env = setEnv( env, "XDM_MANAGED", xma );
1215
if (td->autoLock && cursource == PWSRC_AUTOLOGIN)
1216
env = setEnv( env, "DESKTOP_LOCKED", "true" );
1217
env = setEnv( env, "PATH", curuid ? td->userPath : td->systemPath );
1218
env = setEnv( env, "SHELL", p->pw_shell );
1219
env = setEnv( env, "HOME", p->pw_dir );
1220
#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS)
1221
if (krbtkfile[0] != '\0')
1222
env = setEnv( env, "KRBTKFILE", krbtkfile );
1224
userEnviron = inheritEnv( env, envvars );
1225
env = systemEnv( curuser );
1226
systemEnviron = setEnv( env, "HOME", p->pw_dir );
1227
debug( "user environment:\n%[|''>'\n's"
1228
"system environment:\n%[|''>'\n's"
1229
"end of environments\n",
1234
* for user-based authorization schemes,
1235
* add the user to the server's allowed "hosts" list.
1237
for (i = 0; i < td->authNum; i++) {
1239
if (td->authorizations[i]->name_length == 9 &&
1240
!memcmp( td->authorizations[i]->name, "SUN-DES-1", 9 ))
1243
char netname[MAXNETNAMELEN+1];
1244
char domainname[MAXNETNAMELEN+1];
1246
getdomainname( domainname, sizeof(domainname) );
1247
user2netname( netname, curuid, domainname );
1248
addr.family = FamilyNetname;
1249
addr.length = strlen( netname );
1250
addr.address = netname;
1251
XAddHost( dpy, &addr );
1255
if (td->authorizations[i]->name_length == 14 &&
1256
!memcmp( td->authorizations[i]->name, "MIT-KERBEROS-5", 14 ))
1258
/* Update server's auth file with user-specific info.
1259
* Don't need to AddHost because X server will do that
1260
* automatically when it reads the cache we are about
1263
XauDisposeAuth( td->authorizations[i] );
1264
td->authorizations[i] =
1265
krb5GetAuthFor( 14, "MIT-KERBEROS-5", td->name );
1266
saveServerAuthorizations( td, td->authorizations, td->authNum );
1272
mergeSessionArgs( TRUE );
1274
debug( "now starting the session\n" );
1278
# ifdef HAVE_SETUSERCONTEXT
1279
if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) {
1280
logError( "setusercontext(groups) for %s failed: %m\n",
1285
if (!setGid( curuser, curgid ))
1289
# ifndef HAVE_PAM_GETENVLIST
1290
if (!(pam_env = initStrArr( 0 ))) {
1294
saved_env = environ;
1297
removeCreds = 1; /* set it first - i don't trust PAM's rollback */
1298
pretc = pam_setcred( pamh, 0 );
1300
# ifndef HAVE_PAM_GETENVLIST
1302
environ = saved_env;
1304
# ifdef HAVE_INITGROUPS
1305
/* This seems to be a strange place for it, but do it:
1306
- after the initial groups are set
1307
- after pam_setcred might have set something, even in the error case
1308
- before pam_setcred(DELETE_CRED) might need it
1313
if (pretc != PAM_SUCCESS) {
1314
logError( "pam_setcred() for %s failed: %s\n",
1315
curuser, pam_strerror( pamh, pretc ) );
1320
removeSession = 1; /* set it first - same as above */
1321
pretc = pam_open_session( pamh, 0 );
1323
if (pretc != PAM_SUCCESS) {
1324
logError( "pam_open_session() for %s failed: %s\n",
1325
curuser, pam_strerror( pamh, pretc ) );
1330
/* we don't want sessreg and the startup/reset scripts run with user
1331
credentials. unfortunately, we can reset only the gids. */
1334
# define D_LOGIN_SETGROUP LOGIN_SETGROUP
1336
# define D_LOGIN_SETGROUP 0
1337
#endif /* USE_PAM */
1340
chownCtrl( &td->ctrl, curuid );
1342
#if !defined(USE_PAM) && defined(USESHADOW) && !defined(_AIX)
1345
ctltalk.pipe = &ctlpipe;
1346
ASPrintf( &buf, "sub-daemon for display %s", td->name );
1347
ASPrintf( &buf2, "client for display %s", td->name );
1348
switch (gFork( &ctlpipe, buf, buf2, 0, 0, mstrtalk.pipe, pid )) {
1351
gCloseOnExec( ctltalk.pipe );
1352
if (Setjmp( ctltalk.errjmp ))
1355
gCloseOnExec( mstrtalk.pipe );
1356
if (Setjmp( mstrtalk.errjmp ))
1360
setproctitle( "%s'", td->name );
1362
strApp( &prog, " '", (char *)0 );
1367
sessreg( td, getpid(), curuser, curuid );
1369
/* We do this here, as we want to have the session as parent. */
1370
switch (source( systemEnviron, td->startup, td_setup )) {
1373
case wcCompose( 0, 0, 127 ):
1375
default: /* Explicit failure => message already displayed. */
1376
logError( "Startup script returned non-zero exit code\n" );
1380
/* Memory leaks are ok here as we exec() soon. */
1382
#if defined(USE_PAM) || !defined(_AIX)
1385
/* pass in environment variables set by libpam and modules it called */
1386
# ifdef HAVE_PAM_GETENVLIST
1387
pam_env = pam_getenvlist( pamh );
1391
for (; *pam_env; pam_env++)
1392
userEnviron = putEnv( *pam_env, userEnviron );
1395
# ifdef HAVE_SETLOGIN
1396
if (setlogin( curuser ) < 0) {
1397
logError( "setlogin for %s failed: %m\n", curuser );
1400
# define D_LOGIN_SETLOGIN LOGIN_SETLOGIN
1402
# define D_LOGIN_SETLOGIN 0
1405
# if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1410
# ifndef HAVE_SETUSERCONTEXT
1413
if (!setUid( curuser, curuid ))
1416
if (!setUser( curuser, curuid, curgid ))
1420
# else /* !HAVE_SETUSERCONTEXT */
1423
* Destroy environment.
1424
* We need to do this before setusercontext() because that may
1425
* set or reset some environment variables.
1427
if (!(environ = initStrArr( 0 )))
1431
* Set the user's credentials: uid, gid, groups,
1432
* environment variables, resource limits, and umask.
1434
if (setusercontext( lc, p, p->pw_uid,
1435
LOGIN_SETALL & ~(D_LOGIN_SETGROUP|D_LOGIN_SETLOGIN) ) < 0)
1437
logError( "setusercontext for %s failed: %m\n", curuser );
1441
for (i = 0; environ[i]; i++)
1442
userEnviron = putEnv( environ[i], userEnviron );
1444
# endif /* !HAVE_SETUSERCONTEXT */
1446
#else /* PAM || !_AIX */
1448
* Set the user's credentials: uid, gid, groups,
1449
* audit classes, user limits, and umask.
1451
if (setpcred( curuser, NULL ) == -1) {
1452
logError( "setpcred for %s failed: %m\n", curuser );
1457
* Set the users process environment. Store protected variables and
1458
* obtain updated user environment list. This call will initialize
1461
if (setpenv( curuser, PENV_INIT | PENV_ARGV | PENV_NOEXEC,
1462
userEnviron, NULL ) != 0)
1464
logError( "Can't set %s's process environment\n", curuser );
1467
userEnviron = newenv;
1472
* for user-based authorization schemes,
1473
* use the password to get the user's credentials.
1476
/* do like "keylogin" program */
1478
logInfo( "No password for NIS provided.\n" );
1480
char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1];
1481
int nameret, keyret;
1484
struct key_netstarg netst;
1486
nameret = getnetname( netname );
1487
debug( "user netname: %s\n", netname );
1488
len = strlen( curpass );
1490
bzero( curpass + 8, len - 8 );
1491
keyret = getsecretkey( netname, secretkey, curpass );
1492
debug( "getsecretkey returns %d, key length %d\n",
1493
keyret, strlen( secretkey ) );
1494
netst.st_netname = netname;
1495
memcpy( netst.st_priv_key, secretkey, HEXKEYBYTES );
1496
memset( netst.st_pub_key, 0, HEXKEYBYTES );
1497
if (key_setnet( &netst ) < 0)
1498
debug( "Could not set secret key.\n" );
1499
/* is there a key, and do we have the right password? */
1502
keyret = key_setsecret( secretkey );
1503
debug( "key_setsecret returns %d\n", keyret );
1505
logError( "Failed to set NIS secret key\n" );
1509
/* found a key, but couldn't interpret it */
1510
logError( "Password incorrect for NIS principal %s\n",
1511
nameret ? netname : curuser );
1515
nukeAuth( 9, "SUN-DES-1" );
1516
bzero( secretkey, strlen( secretkey ) );
1520
/* do like "kinit" program */
1522
logInfo( "No password for Kerberos5 provided.\n" );
1524
if ((str = krb5Init( curuser, curpass, td->name )))
1525
userEnviron = setEnv( userEnviron, "KRB5CCNAME", str );
1527
nukeAuth( 14, "MIT-KERBEROS-5" );
1529
if (td->autoReLogin) {
1531
gSendInt( D_ReLogin );
1532
gSendStr( curuser );
1533
gSendStr( curpass );
1534
gSendStr( newdmrc );
1537
bzero( curpass, strlen( curpass ) );
1538
setUserAuthorization( td );
1539
home = getEnv( userEnviron, "HOME" );
1540
if (home && chdir( home ) < 0) {
1541
logError( "Cannot chdir to %s's home %s: %m\n", curuser, home );
1542
sendStr( V_MSG_ERR, "Cannot enter home directory. Using /.\n" );
1544
userEnviron = setEnv( userEnviron, "HOME", "/" );
1547
if (home || td->clientLogFile[0] == '/') {
1548
if (!createClientLog( td->clientLogFile )) {
1549
logWarn( "Session log file according to %s cannot be created: %m\n",
1550
td->clientLogFile );
1555
if (!createClientLog( td->clientLogFallback ))
1556
logError( "Fallback session log file according to %s cannot be created: %m\n",
1557
td->clientLogFallback );
1558
/* Could inform the user, but I guess this is only confusing. */
1561
mergeSessionArgs( home != 0 );
1562
if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 )))
1563
desksess = "failsafe"; /* only due to OOM */
1567
gSendStr( curuser );
1568
gSendStr( desksess );
1569
close( mstrtalk.pipe->fd.w );
1570
userEnviron = setEnv( userEnviron, "DESKTOP_SESSION", desksess );
1571
for (i = 0; td->sessionsDirs[i]; i++) {
1573
if (strApp( &fname, td->sessionsDirs[i], "/", desksess, ".desktop", (char *)0 )) {
1574
if ((str = iniLoad( fname ))) {
1575
if (!strCmp( iniEntry( str, "Desktop Entry", "Hidden", 0 ), "true" ) ||
1576
!(sessargs = iniEntry( str, "Desktop Entry", "Exec", 0 )))
1585
if (!strcmp( desksess, "failsafe" ) ||
1586
!strcmp( desksess, "default" ) ||
1587
!strcmp( desksess, "custom" ))
1588
sessargs = desksess;
1592
if (!(argv = parseArgs( (char **)0, td->session )) ||
1593
!(argv = addStrArr( argv, sessargs, -1 )))
1595
if (argv[0] && *argv[0]) {
1596
debug( "executing session %\"[s\n", argv );
1597
execute( argv, userEnviron );
1598
logError( "Session %\"s execution failed: %m\n", argv[0] );
1600
logError( "Session has no command/arguments\n" );
1601
failsafeArgv[0] = td->failsafeClient;
1602
failsafeArgv[1] = 0;
1603
execute( failsafeArgv, userEnviron );
1604
logError( "Failsafe client %\"s execution failed: %m\n",
1607
sendStr( V_MSG_ERR, 0 );
1613
debug( "StartSession, fork succeeded %d\n", *pid );
1617
if (!Setjmp( ctltalk.errjmp ))
1618
while (gRecvCmd( &i )) {
1620
displayStr( i, buf );
1625
gClosen( ctltalk.pipe );
1632
sessionExit( int status )
1640
switch (source( systemEnviron, td->reset, td_setup )) {
1642
case wcCompose( 0, 0, 127 ):
1645
logError( "Reset script returned non-zero exit code\n" );
1648
sessreg( td, 0, 0, 0 );
1650
switch (Fork( &pid )) {
1652
#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1653
if (restoreGids() && setUid( curuser, curuid ))
1655
if (setUser( curuser, curuid, curgid ))
1659
removeUserAuthorization( td );
1661
krb5Destroy( td->name );
1663
#if !defined(USE_PAM) && !defined(_AIX)
1673
#endif /* !USE_PAM && !_AIX*/
1677
logError( "Cannot clean up session: fork() failed: %m" );
1687
# ifdef HAVE_INITGROUPS
1690
if (removeSession) {
1691
pretc = pam_close_session( pamh, 0 );
1693
if (pretc != PAM_SUCCESS)
1694
logError( "pam_close_session() failed: %s\n",
1695
pam_strerror( pamh, pretc ) );
1697
pretc = pam_setcred( pamh, PAM_DELETE_CRED );
1699
if (pretc != PAM_SUCCESS)
1700
logError( "pam_setcred(DELETE_CRED) failed: %s\n",
1701
pam_strerror( pamh, pretc ) );
1705
pam_end( pamh, PAM_SUCCESS );
1712
/* make sure the server gets reset after the session is over */
1713
if (td->serverPid >= 2) {
1714
if (!td->terminateServer && td->resetSignal)
1715
terminateProcess( td->serverPid, td->resetSignal );
1718
debug( "display %s exiting with status %d\n", td->name, status );
1725
char *data, *fname = 0;
1726
int len, pid, pfd[2], err;
1728
if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam( dmrcuser )))
1732
if (!strApp( &fname, dmrcDir, "/", dmrcuser, ".dmrc", (char *)0 ))
1734
if (!(curdmrc = iniLoad( fname ))) {
1742
if (!strApp( &fname, p->pw_dir, "/.dmrc", (char *)0 ))
1746
switch (Fork( &pid )) {
1752
if (!setUser( p->pw_name, p->pw_uid, p->pw_gid ))
1754
if (!(data = iniLoad( fname ))) {
1755
static const int m1 = -1;
1756
write( pfd[1], &m1, sizeof(int) );
1759
len = strlen( data );
1760
write( pfd[1], &len, sizeof(int) );
1761
write( pfd[1], data, len + 1 );
1767
if (reader( pfd[0], &len, sizeof(int) ) == sizeof(int)) {
1770
else if ((curdmrc = Malloc( len + 1 ))) {
1771
if (reader( pfd[0], curdmrc, len + 1 ) == len + 1)