add more spacing
[personal-kdebase.git] / workspace / kdm / backend / client.c
blob0d8c63e8405d579bc36085af5c3cae4fc59ae150
1 /*
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
11 documentation.
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.
37 #include "dm.h"
38 #include "dm_auth.h"
39 #include "dm_error.h"
41 #include <sys/stat.h>
42 #include <pwd.h>
43 #include <grp.h>
44 #ifdef SECURE_RPC
45 # include <rpc/rpc.h>
46 # include <rpc/key_prot.h>
47 extern int key_setnet( struct key_netstarg *arg );
48 #endif
49 #ifdef K5AUTH
50 # include <krb5/krb5.h>
51 #endif
52 #ifdef HAVE_SETUSERCONTEXT
53 # include <login_cap.h>
54 #endif
55 #ifdef USE_PAM
56 # ifdef HAVE_PAM_PAM_APPL_H
57 # include <pam/pam_appl.h>
58 # else
59 # include <security/pam_appl.h>
60 # endif
61 #elif defined(_AIX) /* USE_PAM */
62 # include <login.h>
63 # include <usersec.h>
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 */
68 # ifdef KERBEROS
69 # include <sys/param.h>
70 # include <krb.h>
71 # ifdef AFS
72 # include <kafs.h>
73 # endif
74 # endif
75 /* for nologin */
76 # include <sys/types.h>
77 # include <unistd.h>
78 /* for expiration */
79 # include <time.h>
80 #endif /* USE_PAM || _AIX */
81 #ifdef HAVE_CKCONNECTOR
82 # include <ck-connector.h>
83 # include <dbus/dbus.h>
84 #endif
85 #ifdef HAVE_GETSPNAM
86 # include <shadow.h>
87 #endif
88 #include <signal.h>
91 * Session data, mostly what struct verify_info was for
93 char *curuser;
94 char *curpass;
95 char *curtype;
96 char *newpass;
97 char **userEnviron;
98 char **systemEnviron;
99 static int curuid;
100 static int curgid;
101 int cursource;
103 char *dmrcuser;
104 char *curdmrc;
105 char *newdmrc;
107 static struct passwd *p;
108 #ifdef HAVE_SETUSERCONTEXT
109 # ifdef HAVE_LOGIN_GETCLASS
110 login_cap_t *lc;
111 # else
112 struct login_cap *lc;
113 # endif
114 #endif
115 #ifdef USE_PAM
116 static pam_handle_t *pamh;
117 static int inAuth;
118 #elif defined(_AIX)
119 static char tty[16], hostname[100];
120 #else
121 # ifdef USESHADOW
122 static struct spwd *sp;
123 # endif
124 # ifdef KERBEROS
125 static char krbtkfile[MAXPATHLEN];
126 # endif
127 #endif
129 static void
130 displayStr( int lv, const char *msg )
132 prepareErrorGreet();
133 gSendInt( lv );
134 gSendStr( msg );
135 gRecvInt();
138 #if !defined(USE_PAM) && (defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW))
139 static void
140 displayMsg( int lv, const char *msg, ... )
142 char *ae;
143 va_list args;
145 va_start( args, msg );
146 VASPrintf( &ae, msg, args );
147 va_end( args );
148 if (ae) {
149 displayStr( lv, ae );
150 free( ae );
153 #endif
155 #ifdef _AIX
156 # define _ENDUSERDB , enduserdb()
157 #else
158 # define _ENDUSERDB
159 #endif
160 #ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
161 # define _ENDSPENT , endspent()
162 #else
163 # define _ENDSPENT
164 #endif
165 #define END_ENT endpwent() _ENDSPENT _ENDUSERDB
167 #define V_RET_NP \
168 do { \
169 END_ENT; \
170 return False; \
171 } while(0)
173 #define V_RET \
174 do { \
175 wipeStr( curpass ); \
176 curpass = 0; \
177 V_RET_NP; \
178 } while(0)
180 #define V_RET_AUTH \
181 do { \
182 prepareErrorGreet(); \
183 gSendInt( V_AUTH ); \
184 V_RET; \
185 } while(0)
187 #define V_RET_FAIL(m) \
188 do { \
189 displayStr( V_MSG_ERR, m ); \
190 gSendInt( V_FAIL ); \
191 V_RET; \
192 } while(0)
194 #ifdef USE_PAM
196 # ifndef PAM_MESSAGE_CONST
197 typedef struct pam_message pam_message_type;
198 typedef void *pam_gi_type;
199 # else
200 typedef const struct pam_message pam_message_type;
201 typedef const void *pam_gi_type;
202 # endif
204 struct pam_data {
205 GConvFunc gconv;
206 int usecur;
207 int abort;
210 static int
211 PAM_conv( int num_msg,
212 pam_message_type **msg,
213 struct pam_response **resp,
214 void *appdata_ptr )
216 int count;
217 struct pam_response *reply;
218 struct pam_data *pd = (struct pam_data *)appdata_ptr;
220 if (!(reply = Calloc( num_msg, sizeof(*reply) )))
221 return PAM_CONV_ERR;
223 reInitErrorLog();
224 debug( "PAM_conv\n" );
225 for (count = 0; count < num_msg; count++)
226 switch (msg[count]->msg_style) {
227 case PAM_TEXT_INFO:
228 debug( " PAM_TEXT_INFO: %s\n", msg[count]->msg );
229 displayStr( inAuth ? V_MSG_INFO_AUTH : V_MSG_INFO, msg[count]->msg );
230 continue;
231 case PAM_ERROR_MSG:
232 debug( " PAM_ERROR_MSG: %s\n", msg[count]->msg );
233 displayStr( V_MSG_ERR, msg[count]->msg );
234 continue;
235 default:
236 /* could do better error handling here, but see below ... */
237 if (pd->usecur) {
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 );
242 if (!curpass)
243 pd->gconv( GCONV_PASS, 0 );
244 strDup( &reply[count].resp, curpass );
245 break;
246 default:
247 logError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
248 goto conv_err;
250 } else {
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 );
255 break;
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 );
259 break;
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 );
264 break;
265 #endif
266 default:
267 logError( "Unknown PAM message style <%d>\n", msg[count]->msg_style );
268 goto conv_err;
271 if (!reply[count].resp) {
272 debug( " PAM_conv aborted\n" );
273 pd->abort = True;
274 goto conv_err;
276 reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
278 debug( " PAM_conv success\n" );
279 *resp = reply;
280 return PAM_SUCCESS;
282 conv_err:
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 );
288 break;
289 #ifdef PAM_BINARY_PROMPT
290 case PAM_BINARY_PROMPT: /* Don't know length, so how wipe? */
291 #endif
292 case PAM_PROMPT_ECHO_ON:
293 free( reply[count].resp );
294 break;
296 free( reply );
297 return PAM_CONV_ERR;
300 # ifdef PAM_FAIL_DELAY
301 static void
302 fail_delay( int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED,
303 void *appdata_ptr ATTR_UNUSED )
305 # endif
307 # ifdef PAM_XDISPLAY
308 /* Inspired by Eamon Walsh's patch for gdm. */
309 static struct pam_xauth_data *
310 getPAMXauthData( const char *xauth_file )
312 FILE *fp;
313 Xauth *auth;
314 struct pam_xauth_data *ret;
316 if (!(fp = fopen( xauth_file, "r" )))
317 return NULL;
319 auth = XauReadAuth( fp );
320 fclose( fp );
321 if (!auth)
322 return NULL;
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 );
335 return ret;
337 # endif
339 static int
340 doPAMAuth( const char *psrv, struct pam_data *pdata )
342 # ifdef PAM_XDISPLAY
343 struct pam_xauth_data *pam_xauth;
344 # endif
345 pam_gi_type pitem;
346 struct pam_conv pconv;
347 int pretc;
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)
354 goto pam_bail2;
355 if ((pretc = pam_set_item( pamh, PAM_TTY, td->name )) != PAM_SUCCESS) {
356 pam_bail:
357 pam_end( pamh, pretc );
358 pamh = 0;
359 pam_bail2:
360 reInitErrorLog();
361 logError( "PAM error: %s\n", pam_strerror( 0, pretc ) );
362 V_RET_FAIL( 0 );
364 if ((td->displayType & d_location) == dForeign) {
365 char *cp = strchr( td->name, ':' );
366 *cp = 0;
367 pretc = pam_set_item( pamh, PAM_RHOST, td->name );
368 *cp = ':';
369 if (pretc != PAM_SUCCESS)
370 goto pam_bail;
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)
374 goto pam_bail;
375 # endif
376 # ifdef PAM_XDISPLAY
377 if ((pretc = pam_set_item( pamh, PAM_XDISPLAY,
378 displayName( td ) )) != PAM_SUCCESS)
379 goto pam_bail;
380 if (td->authFile && (pam_xauth = getPAMXauthData( td->authFile ))) {
381 pretc = pam_set_item( pamh, PAM_XAUTHDATA, pam_xauth );
382 free( pam_xauth );
383 if (pretc != PAM_SUCCESS)
384 goto pam_bail;
386 # endif
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 );
390 # endif
391 reInitErrorLog();
393 inAuth = True;
394 debug( " pam_authenticate() ...\n" );
395 pretc = pam_authenticate( pamh,
396 td->allowNullPasswd ? 0 : PAM_DISALLOW_NULL_AUTHTOK );
397 reInitErrorLog();
398 debug( " pam_authenticate() returned: %s\n", pam_strerror( pamh, pretc ) );
399 inAuth = False;
400 if (pdata->abort) {
401 pam_end( pamh, PAM_SUCCESS );
402 pamh = 0;
403 V_RET;
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
411 * curuser being set.
413 if (!curuser) {
414 debug( " asking PAM for user ...\n" );
415 pam_get_item( pamh, PAM_USER, &pitem );
416 reInitErrorLog();
417 strDup( &curuser, (const char *)pitem );
418 gSendInt( V_PUT_USER );
419 gSendStr( curuser );
421 if (pretc != PAM_SUCCESS) {
422 switch (pretc) {
423 case PAM_USER_UNKNOWN:
424 case PAM_AUTH_ERR:
425 case PAM_MAXTRIES: /* should handle this better ... */
426 case PAM_AUTHINFO_UNAVAIL: /* returned for unknown users ... bogus */
427 pam_end( pamh, pretc );
428 pamh = 0;
429 V_RET_AUTH;
430 default:
431 pam_end( pamh, pretc );
432 pamh = 0;
433 V_RET_FAIL( 0 );
436 return True;
439 #endif /* USE_PAM */
441 static int
442 #if defined(USE_PAM) || defined(_AIX)
443 isNoPassAllowed( const char *un )
445 struct passwd *pw;
446 # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
447 struct spwd *spw;
448 # endif
449 #else
450 isNoPassAllowed( struct passwd *pw )
452 #endif
453 struct group *gr;
454 char **fp;
455 int hg;
457 #if defined(USE_PAM) || defined(_AIX)
458 if (!*un)
459 return False;
460 #endif
462 if (cursource != PWSRC_MANUAL)
463 return True;
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] == '*')
469 return False;
470 # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
471 if ((spw = getspnam( un )) &&
472 (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*'))
473 return False;
474 # endif
475 #endif
477 for (hg = False, fp = td->noPassUsers; *fp; fp++)
478 if (**fp == '@')
479 hg = True;
480 else if (!strcmp( pw->pw_name, *fp ))
481 return True;
482 else if (!strcmp( "*", *fp ) && pw->pw_uid)
483 return True;
485 if (hg) {
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) {
490 endgrent();
491 return True;
493 for (; *gr->gr_mem; gr->gr_mem++)
494 if (!strcmp( pw->pw_name, *gr->gr_mem )) {
495 endgrent();
496 return True;
499 endgrent();
502 return False;
505 #if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT)
506 # define LC_RET0 do { login_close(lc); V_RET; } while(0)
507 #else
508 # define LC_RET0 V_RET
509 #endif
512 verify( GConvFunc gconv, int rootok )
514 #ifdef USE_PAM
515 const char *psrv;
516 struct pam_data pdata;
517 int pretc, pnopass;
518 char psrvb[64];
519 #elif defined(_AIX)
520 char *msg, *curret;
521 int i, reenter;
522 #else
523 struct stat st;
524 const char *nolg;
525 char *buf;
526 int fd;
527 # ifdef HAVE_GETUSERSHELL
528 char *s;
529 # endif
530 # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
531 int tim, expir, warntime, quietlog;
532 # endif
533 #endif
535 debug( "verify ...\n" );
537 #ifdef USE_PAM
539 pnopass = False;
540 if (!strcmp( curtype, "classic" )) {
541 if (!gconv( GCONV_USER, 0 ))
542 return False;
543 if (isNoPassAllowed( curuser )) {
544 gconv( GCONV_PASS_ND, 0 );
545 if (!*curpass) {
546 pnopass = True;
547 sprintf( psrvb, "%.31s-np", PAMService );
548 psrv = psrvb;
549 } else
550 psrv = PAMService;
551 } else
552 psrv = PAMService;
553 pdata.usecur = True;
554 } else {
555 sprintf( psrvb, "%.31s-%.31s", PAMService, curtype );
556 psrv = psrvb;
557 pdata.usecur = False;
559 pdata.gconv = gconv;
560 if (!doPAMAuth( psrv, &pdata ))
561 return False;
563 #elif defined(_AIX)
565 if ((td->displayType & d_location) == dForeign) {
566 char *tmpch;
567 strncpy( hostname, td->name, sizeof(hostname) - 1 );
568 hostname[sizeof(hostname)-1] = '\0';
569 if ((tmpch = strchr( hostname, ':' )))
570 *tmpch = '\0';
571 } else
572 hostname[0] = '\0';
574 /* tty names should only be 15 characters long */
575 # if 0
576 for (i = 0; i < 15 && td->name[i]; i++) {
577 if (td->name[i] == ':' || td->name[i] == '.')
578 tty[i] = '_';
579 else
580 tty[i] = td->name[i];
582 tty[i] = '\0';
583 # else
584 memcpy( tty, "/dev/xdm/", 9 );
585 for (i = 0; i < 6 && td->name[i]; i++) {
586 if (td->name[i] == ':' || td->name[i] == '.')
587 tty[9 + i] = '_';
588 else
589 tty[9 + i] = td->name[i];
591 tty[9 + i] = '\0';
592 # endif
594 if (!strcmp( curtype, "classic" )) {
595 if (!gconv( GCONV_USER, 0 ))
596 return False;
597 if (isNoPassAllowed( curuser )) {
598 gconv( GCONV_PASS_ND, 0 );
599 if (!*curpass) {
600 debug( "accepting despite empty password\n" );
601 goto done;
603 } else
604 if (!gconv( GCONV_PASS, 0 ))
605 V_RET_NP;
606 msg = NULL;
607 if ((i = authenticate( curuser, curpass, &reenter, &msg ))) {
608 debug( "authenticate() failed: %s\n", msg );
609 if (msg)
610 free( msg );
611 loginfailed( curuser, hostname, tty );
612 if (i == ENOENT || i == ESAD)
613 V_RET_AUTH;
614 else
615 V_RET_FAIL( 0 );
617 if (reenter) {
618 logError( "authenticate() requests more data: %s\n", msg );
619 free( msg );
620 V_RET_FAIL( 0 );
622 } else if (!strcmp( curtype, "generic" )) {
623 if (!gconv( GCONV_USER, 0 ))
624 return False;
625 for (curret = 0;;) {
626 msg = NULL;
627 if ((i = authenticate( curuser, curret, &reenter, &msg ))) {
628 debug( "authenticate() failed: %s\n", msg );
629 if (msg)
630 free( msg );
631 loginfailed( curuser, hostname, tty );
632 if (i == ENOENT || i == ESAD)
633 V_RET_AUTH;
634 else
635 V_RET_FAIL( 0 );
637 if (curret)
638 free( curret );
639 if (!reenter)
640 break;
641 if (!(curret = gconv( GCONV_HIDDEN, msg )))
642 return False;
643 free( msg );
645 } else {
646 logError( "Unsupported authentication type %\"s requested\n", curtype );
647 V_RET_FAIL( 0 );
649 if (msg) {
650 displayStr( V_MSG_INFO, msg );
651 free( msg );
654 done:
656 #else
658 if (strcmp( curtype, "classic" )) {
659 logError( "Unsupported authentication type %\"s requested\n", curtype );
660 V_RET_FAIL( 0 );
663 if (!gconv( GCONV_USER, 0 ))
664 return False;
666 if (!(p = getpwnam( curuser ))) {
667 debug( "getpwnam() failed.\n" );
668 gconv( GCONV_PASS, 0 );
669 V_RET_AUTH;
671 if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
672 debug( "account is locked\n" );
673 gconv( GCONV_PASS, 0 );
674 V_RET_AUTH;
677 # ifdef USESHADOW
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 );
683 V_RET_AUTH;
685 } else
686 debug( "getspnam() failed: %m. Are you root?\n" );
687 # endif
689 if (!*p->pw_passwd) {
690 if (!td->allowNullPasswd) {
691 debug( "denying user with empty password\n" );
692 gconv( GCONV_PASS, 0 );
693 V_RET_AUTH;
695 goto nplogin;
698 if (isNoPassAllowed( p )) {
699 nplogin:
700 gconv( GCONV_PASS_ND, 0 );
701 if (!*curpass) {
702 debug( "accepting password-less login\n" );
703 goto done;
705 } else
706 if (!gconv( GCONV_PASS, 0 ))
707 V_RET_NP;
709 # ifdef KERBEROS
710 if (p->pw_uid) {
711 int ret;
712 char realm[REALM_SZ];
714 if (krb_get_lrealm( realm, 1 )) {
715 logError( "Cannot get KerberosIV realm.\n" );
716 V_RET_FAIL( 0 );
719 sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name );
720 krb_set_tkt_string( krbtkfile );
721 unlink( 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" );
727 goto done;
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 );
731 krbtkfile[0] = '\0';
732 V_RET_FAIL( 0 );
734 debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) );
736 krbtkfile[0] = '\0';
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 ))
745 # else
746 if (strcmp( curpass, p->pw_passwd ))
747 # endif
749 debug( "password verify failed\n" );
750 V_RET_AUTH;
753 done:
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 );
762 V_RET_FAIL( 0 );
764 #endif
765 if (!p->pw_uid) {
766 if (!rootok && !td->allowRootLogin)
767 V_RET_FAIL( "Root logins are not allowed" );
768 wipeStr( curpass );
769 curpass = 0;
770 return True; /* don't deny root to log in */
773 #ifdef USE_PAM
775 debug( " pam_acct_mgmt() ...\n" );
776 pretc = pam_acct_mgmt( pamh, 0 );
777 reInitErrorLog();
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 );
785 pamh = 0;
786 gSendInt( V_CHTOK_AUTH );
787 /* this cannot auth the wrong user, as only classic auths get here */
788 while (!doPAMAuth( PAMService, &pdata ))
789 if (pdata.abort)
790 return False;
791 gSendInt( V_PRE_OK );
792 } else
793 gSendInt( V_CHTOK );
794 for (;;) {
795 inAuth = True;
796 debug( " pam_chauthtok() ...\n" );
797 pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK );
798 reInitErrorLog();
799 debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) );
800 inAuth = False;
801 if (pdata.abort) {
802 pam_end( pamh, PAM_SUCCESS );
803 pamh = 0;
804 V_RET;
806 if (pretc == PAM_SUCCESS)
807 break;
808 /* effectively there is only PAM_AUTHTOK_ERR */
809 gSendInt( V_FAIL );
811 wipeStr( curpass );
812 curpass = newpass;
813 newpass = 0;
814 } else if (pretc != PAM_SUCCESS) {
815 pam_end( pamh, pretc );
816 pamh = 0;
817 V_RET_AUTH;
820 #elif defined(_AIX) /* USE_PAM */
822 msg = NULL;
823 if (loginrestrictions( curuser,
824 ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN,
825 tty, &msg ) == -1)
827 debug( "loginrestrictions() - %s\n", msg ? msg : "error" );
828 loginfailed( curuser, hostname, tty );
829 prepareErrorGreet();
830 if (msg) {
831 displayStr( V_MSG_ERR, msg );
832 free( msg );
834 gSendInt( V_AUTH );
835 V_RET;
837 if (msg)
838 free( (void *)msg );
840 #endif /* USE_PAM || _AIX */
842 #ifndef _AIX
844 # ifdef HAVE_SETUSERCONTEXT
845 # ifdef HAVE_LOGIN_GETCLASS
846 lc = login_getclass( p->pw_class );
847 # else
848 lc = login_getpwclass( p );
849 # endif
850 if (!lc)
851 V_RET_FAIL( 0 );
853 p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell );
854 # endif
856 # ifndef USE_PAM
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))
862 if (sp)
863 # endif
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;
875 # else
876 quietlog = False;
877 # ifdef USESHADOW
878 warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN;
879 # else
880 warntime = DEFAULT_WARN;
881 # endif
882 # endif
884 # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
885 if (p->pw_expire) {
886 expir = p->pw_expire / 86400L;
887 # else
888 if (sp->sp_expire != -1) {
889 expir = sp->sp_expire;
890 # endif
891 if (tim > expir) {
892 displayStr( V_MSG_ERR,
893 "Your account has expired;"
894 " please contact your system administrator" );
895 gSendInt( V_FAIL );
896 LC_RET0;
897 } else if (tim > (expir - warntime) && !quietlog) {
898 displayMsg( V_MSG_INFO,
899 "Warning: your account will expire in %d day(s)",
900 expir - tim );
904 # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
905 if (p->pw_change) {
906 expir = p->pw_change / 86400L;
907 # else
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 */
913 gSendInt( V_FAIL );
914 LC_RET0;
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" );
921 gSendInt( V_FAIL );
922 LC_RET0;
924 # endif
925 if (tim > expir) {
926 displayStr( V_MSG_ERR,
927 "You are required to change your password immediately"
928 " (password aged)" );
929 /* XXX todo password change */
930 gSendInt( V_FAIL );
931 LC_RET0;
932 } else if (tim > (expir - warntime) && !quietlog) {
933 displayMsg( V_MSG_INFO,
934 "Warning: your password will expire in %d day(s)",
935 expir - tim );
941 # endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */
943 /* restrict_nologin */
944 # ifndef _PATH_NOLOGIN
945 # define _PATH_NOLOGIN "/etc/nologin"
946 # endif
948 if ((
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 ) ||
953 # endif
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) {
959 close( fd );
960 buf[st.st_size] = 0;
961 displayStr( V_MSG_ERR, buf );
962 free( buf );
963 gSendInt( V_FAIL );
964 LC_RET0;
966 free( buf );
968 close( fd );
970 displayStr( V_MSG_ERR,
971 "Logins are not allowed at the moment.\nTry again later" );
972 gSendInt( V_FAIL );
973 LC_RET0;
976 /* restrict_time */
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" );
981 gSendInt( V_FAIL );
982 LC_RET0;
984 # endif
986 # ifdef HAVE_GETUSERSHELL
987 for (;;) {
988 if (!(s = getusershell())) {
989 debug( "shell not in /etc/shells\n" );
990 endusershell();
991 V_RET_FAIL( "Your login shell is not listed in /etc/shells" );
993 if (!strcmp( s, p->pw_shell )) {
994 endusershell();
995 break;
998 # endif
1000 # endif /* !USE_PAM */
1002 /* restrict_nohome */
1003 # ifdef HAVE_SETUSERCONTEXT
1004 if (login_getcapbool( lc, "requirehome", False )) {
1005 struct stat st;
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" );
1008 gSendInt( V_FAIL );
1009 LC_RET0;
1012 # endif
1014 #endif /* !_AIX */
1016 return True;
1021 static const char *envvars[] = {
1022 "TZ", /* SYSV and SVR4, but never hurts */
1023 #ifdef _AIX
1024 "AUTHSTATE", /* for kerberos */
1025 #endif
1026 NULL
1030 #if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1031 static int num_saved_gids;
1032 static gid_t *saved_gids;
1034 static int
1035 saveGids( void )
1037 num_saved_gids = getgroups( 0, 0 );
1038 if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids )))
1039 return False;
1040 if (getgroups( num_saved_gids, saved_gids ) < 0) {
1041 logError( "saving groups failed: %m\n" );
1042 return False;
1044 return True;
1047 static int
1048 restoreGids( void )
1050 if (setgroups( num_saved_gids, saved_gids ) < 0) {
1051 logError( "restoring groups failed: %m\n" );
1052 return False;
1054 if (setgid( p->pw_gid ) < 0) {
1055 logError( "restoring gid failed: %m\n" );
1056 return False;
1058 return True;
1060 #endif /* USE_PAM && HAVE_INITGROUPS */
1062 static int
1063 resetGids( void )
1065 #ifdef HAVE_INITGROUPS
1066 if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) {
1067 logError( "restoring groups failed: %m\n" );
1068 return False;
1070 #endif
1071 if (setgid( 0 ) < 0) {
1072 logError( "restoring gid failed: %m\n" );
1073 return False;
1075 return True;
1078 static int
1079 setGid( const char *name, int gid )
1081 if (setgid( gid ) < 0) {
1082 logError( "setgid(%d) (user %s) failed: %m\n", gid, name );
1083 return False;
1085 #ifdef HAVE_INITGROUPS
1086 if (initgroups( name, gid ) < 0) {
1087 logError( "initgroups for %s failed: %m\n", name );
1088 setgid( 0 );
1089 return False;
1091 #endif /* QNX4 doesn't support multi-groups, no initgroups() */
1092 return True;
1095 static int
1096 setUid( const char *name, int uid )
1098 if (setuid( uid ) < 0) {
1099 logError( "setuid(%d) (user %s) failed: %m\n", uid, name );
1100 return False;
1102 return True;
1105 static int
1106 setUser( const char *name, int uid, int gid )
1108 if (setGid( name, gid )) {
1109 if (setUid( name, uid ))
1110 return True;
1111 resetGids();
1113 return False;
1116 #if defined(SECURE_RPC) || defined(K5AUTH)
1117 static void
1118 nukeAuth( int len, const char *name )
1120 int i;
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) );
1128 break;
1131 #endif
1133 static void
1134 mergeSessionArgs( int cansave )
1136 char *mfname;
1137 const char *fname;
1138 int i, needsave;
1140 mfname = 0;
1141 fname = ".dmrc";
1142 if ((!curdmrc || newdmrc) && *dmrcDir)
1143 if (strApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 ))
1144 fname = mfname;
1145 needsave = False;
1146 if (!curdmrc) {
1147 curdmrc = iniLoad( fname );
1148 if (!curdmrc) {
1149 strDup( &curdmrc, "[Desktop]\nSession=default\n" );
1150 needsave = True;
1153 if (newdmrc) {
1154 curdmrc = iniMerge( curdmrc, newdmrc );
1155 needsave = True;
1157 if (needsave && cansave)
1158 if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) {
1159 for (i = 0; mfname[i]; i++)
1160 if (mfname[i] == '/') {
1161 mfname[i] = 0;
1162 mkdir( mfname, 0755 );
1163 mfname[i] = '/';
1165 iniSave( curdmrc, mfname );
1167 if (mfname)
1168 free( mfname );
1171 static int
1172 createClientLog( const char *log )
1174 char randstr[32], *randstrp = 0, *lname;
1175 int lfd;
1177 for (;;) {
1178 struct expando macros[] = {
1179 { 'd', 0, td->name },
1180 { 'u', 0, curuser },
1181 { 'r', 0, randstrp },
1182 { 0, 0, 0 }
1184 if (!(lname = expandMacros( log, macros )))
1185 exit( 1 );
1186 unlink( lname );
1187 if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) >= 0) {
1188 dup2( lfd, 1 );
1189 dup2( lfd, 2 );
1190 close( lfd );
1191 free( lname );
1192 return True;
1194 if (errno != EEXIST || !macros[2].uses) {
1195 free( lname );
1196 return False;
1198 logInfo( "Session log file %s not usable, trying another one.\n",
1199 lname );
1200 free( lname );
1201 sprintf( randstr, "%d", secureRandom() );
1202 randstrp = randstr;
1206 static int removeAuth;
1207 #ifdef USE_PAM
1208 static int removeSession;
1209 static int removeCreds;
1210 #endif
1211 #ifdef HAVE_CKCONNECTOR
1212 static CkConnector *ckConnector;
1213 #endif
1215 static GPipe ctlpipe;
1216 static GTalk ctltalk;
1218 static void
1219 sendStr( int lv, const char *msg )
1221 gSet( &ctltalk );
1222 gSendInt( lv );
1223 gSendStr( msg );
1224 gRecvInt();
1228 static void
1229 sendMsg( int lv, const char *msg, ... )
1231 char *ae;
1232 va_list args;
1234 va_start( args, msg );
1235 VASPrintf( &ae, msg, args );
1236 va_end( args );
1237 if (ae) {
1238 sendStr( lv, ae );
1239 free( ae );
1245 startClient( volatile int *pid )
1247 const char *home, *sessargs, *desksess;
1248 char **env, *xma;
1249 char **argv, *fname, *str;
1250 #ifdef USE_PAM
1251 char ** volatile pam_env;
1252 # ifndef HAVE_PAM_GETENVLIST
1253 char **saved_env;
1254 # endif
1255 int pretc;
1256 #else
1257 # ifdef _AIX
1258 char *msg;
1259 char **theenv;
1260 extern char **newenv; /* from libs.a, this is set up by setpenv */
1261 # endif
1262 #endif
1263 #ifdef HAVE_CKCONNECTOR
1264 DBusError error;
1265 int ckStatus;
1266 # ifdef XDMCP
1267 char *remoteHostName = 0;
1268 const char *spaceStr = "";
1269 # endif
1270 char ckDeviceBuf[20] = "";
1271 const char *ckDevice = ckDeviceBuf;
1272 dbus_bool_t isLocal;
1273 #endif
1274 char *failsafeArgv[2];
1275 char *buf, *buf2;
1276 int i;
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 );
1286 pError:
1287 displayStr( V_MSG_ERR, 0 );
1288 V_RET;
1290 #endif
1292 strcpy( curuser, p->pw_name ); /* Use normalized login name. */
1294 #ifdef HAVE_CKCONNECTOR
1295 if (!(ckConnector = ck_connector_new())) {
1296 logOutOfMem();
1297 V_RET;
1300 # ifdef HAVE_VTS
1301 if (td->serverVT > 0)
1302 sprintf( ckDeviceBuf, "/dev/tty%d", td->serverVT );
1303 # endif
1304 isLocal = ((td->displayType & d_location) == dLocal);
1305 # ifdef XDMCP
1306 if (!isLocal) {
1307 int length, family;
1308 CARD8 *data;
1309 ARRAY8 addr;
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);
1319 # endif
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 */
1327 # ifdef XDMCP
1328 "remote-host-name", remoteHostName ?
1329 (const char **)&remoteHostName : &spaceStr,
1330 # endif
1331 NULL );
1332 # ifdef XDMCP
1333 if (remoteHostName)
1334 free( remoteHostName );
1335 # endif
1336 debug( "ck status: %d\n", ckStatus );
1337 if (!ckStatus) {
1338 if (dbus_error_is_set( &error )) {
1339 logError( "Cannot open ConsoleKit session: %s\n", error.message );
1340 dbus_error_free( &error );
1341 } else {
1342 logError( "Cannot open ConsoleKit session, likely OOM\n" );
1344 ck_connector_unref( ckConnector );
1345 V_RET;
1347 #endif
1349 #ifndef USE_PAM
1350 # ifdef _AIX
1351 msg = NULL;
1352 loginsuccess( curuser, hostname, tty, &msg );
1353 if (msg) {
1354 debug( "loginsuccess() - %s\n", msg );
1355 free( (void *)msg );
1357 # else /* _AIX */
1358 # if defined(KERBEROS) && defined(AFS)
1359 if (krbtkfile[0] != '\0') {
1360 if (k_hasafs()) {
1361 int fail = False;
1362 if (k_setpag() == -1) {
1363 logError( "setpag() for %s failed\n", curuser );
1364 fail = True;
1366 if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS) {
1367 logError( "AFS Warning: %s\n", krb_get_err_text( ret ) );
1368 fail = True;
1370 if (fail)
1371 displayMsg( V_MSG_ERR,
1372 "Warning: Problems during Kerberos4/AFS setup." );
1375 # endif /* KERBEROS && AFS */
1376 # endif /* _AIX */
1377 #endif /* !PAM */
1379 curuid = p->pw_uid;
1380 curgid = p->pw_gid;
1382 env = baseEnv( 0, curuser );
1383 xma = 0;
1384 strApp( &xma, "method=", curtype, (char *)0 );
1385 if (td_setup)
1386 strApp( &xma, ",auto", (char *)0 );
1387 if (xma) {
1388 env = setEnv( env, "XDM_MANAGED", xma );
1389 free( 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 );
1399 #endif
1400 #ifdef HAVE_CKCONNECTOR
1401 env = setEnv( env, "XDG_SESSION_COOKIE",
1402 ck_connector_get_cookie( ckConnector ) );
1403 #endif
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",
1410 userEnviron,
1411 systemEnviron );
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++) {
1418 #ifdef SECURE_RPC
1419 if (td->authorizations[i]->name_length == 9 &&
1420 !memcmp( td->authorizations[i]->name, "SUN-DES-1", 9 ))
1422 XHostAddress addr;
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 );
1433 #endif
1434 #ifdef K5AUTH
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
1441 * to point it at.
1443 XauDisposeAuth( td->authorizations[i] );
1444 td->authorizations[i] =
1445 krb5GetAuthFor( 14, "MIT-KERBEROS-5", td->name );
1446 saveServerAuthorizations( td, td->authorizations, td->authNum );
1448 #endif
1451 if (*dmrcDir)
1452 mergeSessionArgs( True );
1454 debug( "now starting the session\n" );
1456 #ifdef USE_PAM
1458 # ifdef HAVE_SETUSERCONTEXT
1459 if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) {
1460 logError( "setusercontext(groups) for %s failed: %m\n",
1461 curuser );
1462 goto pError;
1464 # else
1465 if (!setGid( curuser, curgid ))
1466 goto pError;
1467 # endif
1469 # ifndef HAVE_PAM_GETENVLIST
1470 if (!(pam_env = initStrArr( 0 ))) {
1471 resetGids();
1472 goto pError;
1474 saved_env = environ;
1475 environ = pam_env;
1476 # endif
1477 removeCreds = True; /* set it first - i don't trust PAM's rollback */
1478 pretc = pam_setcred( pamh, 0 );
1479 reInitErrorLog();
1480 # ifndef HAVE_PAM_GETENVLIST
1481 pam_env = environ;
1482 environ = saved_env;
1483 # endif
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
1490 if (!saveGids())
1491 goto pError;
1492 # endif
1493 if (pretc != PAM_SUCCESS) {
1494 logError( "pam_setcred() for %s failed: %s\n",
1495 curuser, pam_strerror( pamh, pretc ) );
1496 resetGids();
1497 V_RET;
1500 removeSession = True; /* set it first - same as above */
1501 pretc = pam_open_session( pamh, 0 );
1502 reInitErrorLog();
1503 if (pretc != PAM_SUCCESS) {
1504 logError( "pam_open_session() for %s failed: %s\n",
1505 curuser, pam_strerror( pamh, pretc ) );
1506 resetGids();
1507 V_RET;
1510 /* we don't want sessreg and the startup/reset scripts run with user
1511 credentials. unfortunately, we can reset only the gids. */
1512 resetGids();
1514 # define D_LOGIN_SETGROUP LOGIN_SETGROUP
1515 #else /* USE_PAM */
1516 # define D_LOGIN_SETGROUP 0
1517 #endif /* USE_PAM */
1519 removeAuth = True;
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 )) {
1525 case 0:
1527 gCloseOnExec( ctltalk.pipe );
1528 if (Setjmp( ctltalk.errjmp ))
1529 exit( 1 );
1531 gCloseOnExec( mstrtalk.pipe );
1532 if (Setjmp( mstrtalk.errjmp ))
1533 goto cError;
1535 #ifndef NOXDMTITLE
1536 setproctitle( "%s'", td->name );
1537 #endif
1538 strApp( &prog, " '", (char *)0 );
1539 reInitErrorLog();
1541 setsid();
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 )) {
1548 case 0:
1549 break;
1550 case wcCompose( 0, 0, 127 ):
1551 goto cError;
1552 default: /* Explicit failure => message already displayed. */
1553 logError( "Startup script returned non-zero exit code\n" );
1554 exit( 1 );
1557 /* Memory leaks are ok here as we exec() soon. */
1559 #if defined(USE_PAM) || !defined(_AIX)
1561 # ifdef USE_PAM
1562 /* pass in environment variables set by libpam and modules it called */
1563 # ifdef HAVE_PAM_GETENVLIST
1564 pam_env = pam_getenvlist( pamh );
1565 reInitErrorLog();
1566 # endif
1567 if (pam_env)
1568 for (; *pam_env; pam_env++)
1569 userEnviron = putEnv( *pam_env, userEnviron );
1570 # endif
1572 # ifdef HAVE_SETLOGIN
1573 if (setlogin( curuser ) < 0) {
1574 logError( "setlogin for %s failed: %m\n", curuser );
1575 goto cError;
1577 # define D_LOGIN_SETLOGIN LOGIN_SETLOGIN
1578 # else
1579 # define D_LOGIN_SETLOGIN 0
1580 # endif
1582 # if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1583 if (!restoreGids())
1584 goto cError;
1585 # endif
1587 # ifndef HAVE_SETUSERCONTEXT
1589 # ifdef USE_PAM
1590 if (!setUid( curuser, curuid ))
1591 goto cError;
1592 # else
1593 if (!setUser( curuser, curuid, curgid ))
1594 goto cError;
1595 # endif
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 )))
1605 goto cError;
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 );
1615 goto cError;
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 );
1630 goto cError;
1634 * Set the users process environment. Store protected variables and
1635 * obtain updated user environment list. This call will initialize
1636 * global 'newenv'.
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 );
1644 goto cError;
1646 userEnviron = newenv;
1648 #endif /* _AIX */
1651 * for user-based authorization schemes,
1652 * use the password to get the user's credentials.
1654 #ifdef SECURE_RPC
1655 /* do like "keylogin" program */
1656 if (!curpass[0])
1657 logInfo( "No password for NIS provided.\n" );
1658 else {
1659 char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1];
1660 int nameret, keyret;
1661 int len;
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 );
1668 if (len > 8)
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? */
1679 if (keyret == 1) {
1680 if (*secretkey) {
1681 keyret = key_setsecret( secretkey );
1682 debug( "key_setsecret returns %d\n", keyret );
1683 if (keyret == -1)
1684 logError( "Failed to set NIS secret key\n" );
1685 else
1686 key_set_ok = True;
1687 } else {
1688 /* found a key, but couldn't interpret it */
1689 logError( "Password incorrect for NIS principal %s\n",
1690 nameret ? netname : curuser );
1693 if (!key_set_ok)
1694 nukeAuth( 9, "SUN-DES-1" );
1695 bzero( secretkey, strlen( secretkey ) );
1697 #endif
1698 #ifdef K5AUTH
1699 /* do like "kinit" program */
1700 if (!curpass[0])
1701 logInfo( "No password for Kerberos5 provided.\n" );
1702 else
1703 if ((str = krb5Init( curuser, curpass, td->name )))
1704 userEnviron = setEnv( userEnviron, "KRB5CCNAME", str );
1705 else
1706 nukeAuth( 14, "MIT-KERBEROS-5" );
1707 #endif /* K5AUTH */
1708 if (td->autoReLogin) {
1709 gSet( &mstrtalk );
1710 gSendInt( D_ReLogin );
1711 gSendStr( curuser );
1712 gSendStr( curpass );
1713 gSendStr( newdmrc );
1715 if (curpass)
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" );
1722 chdir( "/" );
1723 userEnviron = setEnv( userEnviron, "HOME", "/" );
1724 home = 0;
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 );
1730 goto tmperr;
1732 } else {
1733 tmperr:
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. */
1739 if (!*dmrcDir)
1740 mergeSessionArgs( home != 0 );
1741 if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 )))
1742 desksess = "failsafe"; /* only due to OOM */
1743 gSet( &mstrtalk );
1744 gSendInt( D_User );
1745 gSendInt( curuid );
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++) {
1751 fname = 0;
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 )))
1756 sessargs = "";
1757 free( str );
1758 free( fname );
1759 goto gotit;
1761 free( fname );
1764 if (!strcmp( desksess, "failsafe" ) ||
1765 !strcmp( desksess, "default" ) ||
1766 !strcmp( desksess, "custom" ))
1767 sessargs = desksess;
1768 else
1769 sessargs = "";
1770 gotit:
1771 if (!(argv = parseArgs( (char **)0, td->session )) ||
1772 !(argv = addStrArr( argv, sessargs, -1 )))
1773 exit( 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] );
1778 } else
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",
1784 failsafeArgv[0] );
1785 cError:
1786 sendStr( V_MSG_ERR, 0 );
1787 exit( 1 );
1788 case -1:
1789 free( buf );
1790 V_RET;
1792 debug( "StartSession, fork succeeded %d\n", *pid );
1793 free( buf );
1794 wipeStr( curpass );
1795 curpass = 0;
1796 END_ENT;
1798 gSet( &ctltalk );
1799 if (!Setjmp( ctltalk.errjmp ))
1800 while (gRecvCmd( &i )) {
1801 buf = gRecvStr();
1802 displayStr( i, buf );
1803 free( buf );
1804 gSet( &ctltalk );
1805 gSendInt( 0 );
1807 gClosen( ctltalk.pipe );
1808 finishGreet();
1810 return True;
1813 void
1814 clientExited( void )
1816 int pid;
1817 #ifdef USE_PAM
1818 int pretc;
1819 #endif
1820 #ifdef HAVE_CKCONNECTOR
1821 DBusError error;
1822 #endif
1824 if (removeAuth) {
1825 switch (source( systemEnviron, td->reset, td_setup )) {
1826 case 0:
1827 case wcCompose( 0, 0, 127 ):
1828 break;
1829 default:
1830 logError( "Reset script returned non-zero exit code\n" );
1831 break;
1833 sessreg( td, 0, 0, 0 );
1835 switch (Fork( &pid )) {
1836 case 0:
1837 #if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1838 if (restoreGids() && setUid( curuser, curuid ))
1839 #else
1840 if (setUser( curuser, curuid, curgid ))
1841 #endif
1844 removeUserAuthorization( td );
1845 #ifdef K5AUTH
1846 krb5Destroy( td->name );
1847 #endif /* K5AUTH */
1848 #if !defined(USE_PAM) && !defined(_AIX)
1849 # ifdef KERBEROS
1850 if (krbtkfile[0]) {
1851 (void)dest_tkt();
1852 # ifdef AFS
1853 if (k_hasafs())
1854 (void)k_unlog();
1855 # endif
1857 # endif
1858 #endif /* !USE_PAM && !_AIX*/
1860 exit( 0 );
1861 case -1:
1862 logError( "Cannot clean up session: fork() failed: %m" );
1863 break;
1864 default:
1865 Wait4( &pid );
1866 break;
1868 removeAuth = False;
1871 #ifdef USE_PAM
1872 if (removeCreds) {
1873 # ifdef HAVE_INITGROUPS
1874 restoreGids();
1875 # endif
1876 if (removeSession) {
1877 pretc = pam_close_session( pamh, 0 );
1878 reInitErrorLog();
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 );
1884 reInitErrorLog();
1885 if (pretc != PAM_SUCCESS)
1886 logError( "pam_setcred(DELETE_CRED) failed: %s\n",
1887 pam_strerror( pamh, pretc ) );
1888 resetGids();
1889 removeCreds = False;
1891 if (pamh) {
1892 pam_end( pamh, PAM_SUCCESS );
1893 reInitErrorLog();
1894 pamh = 0;
1896 #endif
1898 #ifdef HAVE_CKCONNECTOR
1899 if (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 );
1905 } else {
1906 logError( "Cannot close ConsoleKit session, likely OOM\n" );
1909 ck_connector_unref( ckConnector );
1910 ckConnector = 0;
1912 #endif
1915 void
1916 sessionExit( int status )
1918 Signal( SIGTERM, SIG_IGN );
1920 clientExited();
1922 finishGreet();
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 );
1928 } else
1929 resetServer( td );
1930 debug( "display %s exiting with status %d\n", td->name, status );
1931 exit( status );
1935 readDmrc()
1937 char *data, *fname = 0;
1938 int len, pid, pfd[2], err;
1940 endpwent();
1941 if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam( dmrcuser )))
1942 return GE_NoUser;
1944 if (*dmrcDir) {
1945 if (!strApp( &fname, dmrcDir, "/", p->pw_name, ".dmrc", (char *)0 ))
1946 return GE_Error;
1947 if (!(curdmrc = iniLoad( fname ))) {
1948 free( fname );
1949 return GE_Ok;
1951 free( fname );
1952 return GE_NoFile;
1955 if (!strApp( &fname, p->pw_dir, "/.dmrc", (char *)0 ))
1956 return GE_Error;
1957 if (pipe( pfd ))
1958 return GE_Error;
1959 switch (Fork( &pid )) {
1960 case -1:
1961 close( pfd[0] );
1962 close( pfd[1] );
1963 return GE_Error;
1964 case 0:
1965 if (!setUser( p->pw_name, p->pw_uid, p->pw_gid ))
1966 exit( 0 );
1967 if (!(data = iniLoad( fname ))) {
1968 static const int m1 = -1;
1969 write( pfd[1], &m1, sizeof(int) );
1970 exit( 0 );
1972 len = strlen( data );
1973 write( pfd[1], &len, sizeof(int) );
1974 write( pfd[1], data, len + 1 );
1975 exit( 0 );
1977 close( pfd[1] );
1978 free( fname );
1979 err = GE_Error;
1980 if (reader( pfd[0], &len, sizeof(int) ) == sizeof(int)) {
1981 if (len == -1)
1982 err = GE_Denied;
1983 else if ((curdmrc = Malloc( len + 1 ))) {
1984 if (reader( pfd[0], curdmrc, len + 1 ) == len + 1)
1985 err = GE_Ok;
1986 else {
1987 free( curdmrc );
1988 curdmrc = 0;
1992 close( pfd[0] );
1993 Wait4( &pid );
1994 return err;