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);
48
# include <X11/Xlib.h>
51
# include <krb5/krb5.h>
53
#ifdef HAVE_SETUSERCONTEXT
54
# include <login_cap.h>
57
# ifdef HAVE_PAM_PAM_APPL_H
58
# include <pam/pam_appl.h>
60
# include <security/pam_appl.h>
62
#elif defined(_AIX) /* USE_PAM */
65
extern int loginrestrictions(const char *Name, const int Mode, const char *Tty, char **Msg);
66
extern int loginfailed(const char *User, const char *Host, const char *Tty);
67
extern int loginsuccess(const char *User, const char *Host, const char *Tty, char **Msg);
68
#else /* USE_PAM || _AIX */
70
# include <sys/param.h>
77
# include <sys/types.h>
81
#endif /* USE_PAM || _AIX */
82
#ifdef HAVE_CKCONNECTOR
83
# include <ck-connector.h>
84
# include <dbus/dbus.h>
92
* Session data, mostly what struct verify_info was for
108
static struct passwd *p;
109
#ifdef HAVE_SETUSERCONTEXT
110
# ifdef HAVE_LOGIN_GETCLASS
113
struct login_cap *lc;
117
static pam_handle_t *pamh;
120
static char tty[16], hostname[100];
123
static struct spwd *sp;
126
static char krbtkfile[MAXPATHLEN];
131
displayStr(int lv, const char *msg)
139
#if (!defined(USE_PAM) && !defined(_AIX) \
140
&& (defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) \
141
|| (defined(KERBEROS) && defined(AFS)))) \
142
|| defined(HAVE_CKCONNECTOR)
144
displayMsg(int lv, const char *msg, ...)
150
VASPrintf(&ae, msg, args);
160
# define _ENDUSERDB , enduserdb()
164
#ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */
165
# define _ENDSPENT , endspent()
169
#define END_ENT endpwent() _ENDSPENT _ENDUSERDB
186
prepareErrorGreet(); \
191
#define V_RET_FAIL(m) \
193
displayStr(V_MSG_ERR, m); \
200
# ifndef PAM_MESSAGE_CONST
201
typedef struct pam_message pam_message_type;
202
typedef void *pam_gi_type;
204
typedef const struct pam_message pam_message_type;
205
typedef const void *pam_gi_type;
215
PAM_conv(int num_msg,
216
pam_message_type **msg,
217
struct pam_response **resp,
221
struct pam_response *reply;
222
struct pam_data *pd = (struct pam_data *)appdata_ptr;
224
if (!(reply = Calloc(num_msg, sizeof(*reply))))
229
for (count = 0; count < num_msg; count++)
230
switch (msg[count]->msg_style) {
232
debug(" PAM_TEXT_INFO: %s\n", msg[count]->msg);
233
displayStr(inAuth ? V_MSG_INFO_AUTH : V_MSG_INFO, msg[count]->msg);
236
debug(" PAM_ERROR_MSG: %s\n", msg[count]->msg);
237
displayStr(V_MSG_ERR, msg[count]->msg);
240
/* could do better error handling here, but see below ... */
242
switch (msg[count]->msg_style) {
243
/* case PAM_PROMPT_ECHO_ON: cannot happen */
244
case PAM_PROMPT_ECHO_OFF:
245
debug(" PAM_PROMPT_ECHO_OFF (usecur): %s\n", msg[count]->msg);
247
pd->gconv(GCONV_PASS, 0);
248
strDup(&reply[count].resp, curpass);
251
logError("Unknown PAM message style <%d>\n", msg[count]->msg_style);
255
switch (msg[count]->msg_style) {
256
case PAM_PROMPT_ECHO_ON:
257
debug(" PAM_PROMPT_ECHO_ON: %s\n", msg[count]->msg);
258
reply[count].resp = pd->gconv(GCONV_NORMAL, msg[count]->msg);
260
case PAM_PROMPT_ECHO_OFF:
261
debug(" PAM_PROMPT_ECHO_OFF: %s\n", msg[count]->msg);
262
reply[count].resp = pd->gconv(GCONV_HIDDEN, msg[count]->msg);
264
#ifdef PAM_BINARY_PROMPT
265
case PAM_BINARY_PROMPT:
266
debug(" PAM_BINARY_PROMPT\n");
267
reply[count].resp = pd->gconv(GCONV_BINARY, msg[count]->msg);
271
logError("Unknown PAM message style <%d>\n", msg[count]->msg_style);
275
if (!reply[count].resp) {
276
debug(" PAM_conv aborted\n");
280
reply[count].resp_retcode = PAM_SUCCESS; /* unused in linux-pam */
282
debug(" PAM_conv success\n");
287
for (; count >= 0; count--)
288
if (reply[count].resp)
289
switch (msg[count]->msg_style) {
290
case PAM_PROMPT_ECHO_OFF:
291
wipeStr(reply[count].resp);
293
#ifdef PAM_BINARY_PROMPT
294
case PAM_BINARY_PROMPT: /* Don't know length, so how wipe? */
296
case PAM_PROMPT_ECHO_ON:
297
free(reply[count].resp);
304
# ifdef PAM_FAIL_DELAY
306
fail_delay(int retval ATTR_UNUSED, unsigned usec_delay ATTR_UNUSED,
307
void *appdata_ptr ATTR_UNUSED)
312
static struct pam_xauth_data *
313
getPAMXauthData(void)
316
struct pam_xauth_data *ret;
318
for (i = 0; i < td->authNum; i++) {
319
Xauth *auth = td->authorizations[i];
320
if (auth->data_length > 0) {
321
if ((ret = malloc(sizeof(*ret) + auth->name_length + 1 + auth->data_length))) {
322
ret->name = (char *)ret + sizeof(*ret);
323
ret->namelen = auth->name_length;
324
memcpy(ret->name, auth->name, auth->name_length);
325
ret->name[ret->namelen] = 0;
326
ret->data = ret->name + ret->namelen + 1;
327
ret->datalen = auth->data_length;
328
memcpy(ret->data, auth->data, auth->data_length);
338
doPAMAuth(const char *psrv, struct pam_data *pdata)
341
struct pam_xauth_data *pam_xauth;
344
struct pam_conv pconv;
347
pdata->abort = False;
348
pconv.conv = PAM_conv;
349
pconv.appdata_ptr = (void *)pdata;
350
debug(" PAM service %s\n", psrv);
351
if ((pretc = pam_start(psrv, curuser, &pconv, &pamh)) != PAM_SUCCESS)
353
if ((pretc = pam_set_item(pamh, PAM_TTY, td->name)) != PAM_SUCCESS) {
355
pam_end(pamh, pretc);
359
logError("PAM error: %s\n", pam_strerror(0, pretc));
362
if ((td->displayType & d_location) == dForeign) {
363
char *cp = strchr(td->name, ':');
365
pretc = pam_set_item(pamh, PAM_RHOST, td->name);
367
if (pretc != PAM_SUCCESS)
370
# ifdef __sun__ /* Only Solaris <= 9, but checking it does not seem worth it. */
371
else if ((pretc = pam_set_item(pamh, PAM_RHOST, 0)) != PAM_SUCCESS)
375
if ((pretc = pam_set_item(pamh, PAM_XDISPLAY,
376
displayName(td))) != PAM_SUCCESS) {
377
debug("setting PAM_XDISPLAY failed: %s\n",
378
pam_strerror(0, pretc));
379
} else if ((pam_xauth = getPAMXauthData())) {
380
pretc = pam_set_item(pamh, PAM_XAUTHDATA, pam_xauth);
382
if (pretc != PAM_SUCCESS)
383
debug("setting PAM_XAUTHDATA failed: %s\n",
384
pam_strerror(0, pretc));
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);
545
if (!curpass || !*curpass) {
547
sprintf(psrvb, "%.31s-np", PAMService);
557
sprintf(psrvb, "%.31s-%.31s", PAMService, curtype);
559
pdata.usecur = False;
562
if (!doPAMAuth(psrv, &pdata))
567
if ((td->displayType & d_location) == dForeign) {
569
strncpy(hostname, td->name, sizeof(hostname) - 1);
570
hostname[sizeof(hostname)-1] = '\0';
571
if ((tmpch = strchr(hostname, ':')))
577
/* tty names should only be 15 characters long */
579
for (i = 0; i < 15 && td->name[i]; i++) {
580
if (td->name[i] == ':' || td->name[i] == '.')
583
tty[i] = td->name[i];
587
memcpy(tty, "/dev/xdm/", 9);
588
for (i = 0; i < 6 && td->name[i]; i++) {
589
if (td->name[i] == ':' || td->name[i] == '.')
592
tty[9 + i] = td->name[i];
597
if (!strcmp(curtype, "classic")) {
598
if (!gconv(GCONV_USER, 0))
600
if (isNoPassAllowed(curuser)) {
601
gconv(GCONV_PASS_ND, 0);
603
debug("accepting despite empty password\n");
607
if (!gconv(GCONV_PASS, 0))
611
if ((i = authenticate(curuser, curpass, &reenter, &msg))) {
612
debug("authenticate() failed: %s\n", msg);
614
loginfailed(curuser, hostname, tty);
615
if (i == ENOENT || i == ESAD)
621
logError("authenticate() requests more data: %s\n", msg);
625
} else if (!strcmp(curtype, "generic")) {
626
if (!gconv(GCONV_USER, 0))
630
if ((i = authenticate(curuser, curret, &reenter, &msg))) {
631
debug("authenticate() failed: %s\n", msg);
633
loginfailed(curuser, hostname, tty);
634
if (i == ENOENT || i == ESAD)
642
if (!(curret = gconv(GCONV_HIDDEN, msg)))
647
logError("Unsupported authentication type %\"s requested\n", curtype);
651
displayStr(V_MSG_INFO, msg);
659
if (strcmp(curtype, "classic")) {
660
logError("Unsupported authentication type %\"s requested\n", curtype);
664
if (!gconv(GCONV_USER, 0))
667
if (!(p = getpwnam(curuser))) {
668
debug("getpwnam() failed.\n");
669
gconv(GCONV_PASS, 0);
672
if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
673
debug("account is locked\n");
674
gconv(GCONV_PASS, 0);
679
if ((sp = getspnam(curuser))) {
680
p->pw_passwd = sp->sp_pwdp;
681
if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') {
682
debug("account is locked\n");
683
gconv(GCONV_PASS, 0);
687
debug("getspnam() failed: %m. Are you root?\n");
691
if (!*p->pw_passwd) {
692
if (!td->allowNullPasswd) {
693
debug("denying user with empty password\n");
694
gconv(GCONV_PASS, 0);
700
if (isNoPassAllowed(p)) {
702
gconv(GCONV_PASS_ND, 0);
704
debug("accepting password-less login\n");
708
if (!gconv(GCONV_PASS, 0))
715
char realm[REALM_SZ];
717
if (krb_get_lrealm(realm, 1)) {
718
logError("Cannot get KerberosIV realm.\n");
722
sprintf(krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen(TKT_ROOT) - 2, td->name);
723
krb_set_tkt_string(krbtkfile);
726
ret = krb_verify_user(curuser, "", realm, curpass, 1, "rcmd");
727
if (ret == KSUCCESS) {
728
chown(krbtkfile, p->pw_uid, p->pw_gid);
729
debug("KerberosIV verify succeeded\n");
731
} else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) {
732
logError("KerberosIV verification failure %\"s for %s\n",
733
krb_get_err_text(ret), curuser);
737
debug("KerberosIV verify failed: %s\n", krb_get_err_text(ret));
740
# endif /* KERBEROS */
742
# if defined(ultrix) || defined(__ultrix__)
743
if (authenticate_user(p, curpass, 0) < 0)
744
# elif defined(HAVE_PW_ENCRYPT)
745
if (strcmp(pw_encrypt(curpass, p->pw_passwd), p->pw_passwd))
746
# elif defined(HAVE_CRYPT)
747
if (strcmp(crypt(curpass, p->pw_passwd), p->pw_passwd))
749
if (strcmp(curpass, p->pw_passwd))
752
debug("password verify failed\n");
758
#endif /* !defined(USE_PAM) && !defined(_AIX) */
760
debug("restrict %s ...\n", curuser);
762
#if defined(USE_PAM) || defined(_AIX)
763
if (!(p = getpwnam(curuser))) {
764
logError("getpwnam(%s) failed.\n", curuser);
769
if (!rootok && !td->allowRootLogin)
770
V_RET_FAIL("Root logins are not allowed");
773
return True; /* don't deny root to log in */
778
debug(" pam_acct_mgmt() ...\n");
779
pretc = pam_acct_mgmt(pamh, 0);
781
debug(" pam_acct_mgmt() returned: %s\n", pam_strerror(pamh, pretc));
782
if (pretc == PAM_NEW_AUTHTOK_REQD) {
783
pdata.usecur = False;
784
pdata.gconv = conv_interact;
785
/* pam will have output a message already, so no prepareErrorGreet() */
786
if (gconv != conv_interact || pnopass) {
787
pam_end(pamh, PAM_SUCCESS);
789
gSendInt(V_CHTOK_AUTH);
790
/* this cannot auth the wrong user, as only classic auths get here */
791
while (!doPAMAuth(PAMService, &pdata))
800
debug(" pam_chauthtok() ...\n");
801
pretc = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
803
debug(" pam_chauthtok() returned: %s\n", pam_strerror(pamh, pretc));
806
pam_end(pamh, PAM_SUCCESS);
810
if (pretc == PAM_SUCCESS)
812
/* effectively there is only PAM_AUTHTOK_ERR */
818
} else if (pretc != PAM_SUCCESS) {
819
pam_end(pamh, pretc);
824
#elif defined(_AIX) /* USE_PAM */
827
if (loginrestrictions(curuser,
828
((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN,
830
debug("loginrestrictions() - %s\n", msg ? msg : "error");
831
loginfailed(curuser, hostname, tty);
834
displayStr(V_MSG_ERR, msg);
842
#endif /* USE_PAM || _AIX */
846
# ifdef HAVE_SETUSERCONTEXT
847
# ifdef HAVE_LOGIN_GETCLASS
848
lc = login_getclass(p->pw_class);
850
lc = login_getpwclass(p);
855
p->pw_shell = login_getcapstr(lc, "shell", p->pw_shell, p->pw_shell);
860
/* restrict_expired */
861
# if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW)
863
# if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW))
868
# define DEFAULT_WARN (2L * 7L) /* Two weeks */
870
tim = time(0) / 86400L;
872
# ifdef HAVE_SETUSERCONTEXT
873
quietlog = login_getcapbool(lc, "hushlogin", False);
874
warntime = login_getcaptime(lc, "warnexpire",
875
DEFAULT_WARN * 86400L,
876
DEFAULT_WARN * 86400L) / 86400L;
880
warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN;
882
warntime = DEFAULT_WARN;
886
# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
888
expir = p->pw_expire / 86400L;
890
if (sp->sp_expire != -1) {
891
expir = sp->sp_expire;
894
displayStr(V_MSG_ERR,
895
"Your account has expired;"
896
" please contact your system administrator");
899
} else if (tim > (expir - warntime) && !quietlog) {
900
displayMsg(V_MSG_INFO,
901
"Warning: your account will expire in %d day(s)",
906
# ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE
908
expir = p->pw_change / 86400L;
910
if (!sp->sp_lstchg) {
911
displayStr(V_MSG_ERR,
912
"You are required to change your password immediately"
914
/* XXX todo password change */
917
} else if (sp->sp_max != -1) {
918
expir = sp->sp_lstchg + sp->sp_max;
919
if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) {
920
displayStr(V_MSG_ERR,
921
"Your account has expired;"
922
" please contact your system administrator");
928
displayStr(V_MSG_ERR,
929
"You are required to change your password immediately"
931
/* XXX todo password change */
934
} else if (tim > (expir - warntime) && !quietlog) {
935
displayMsg(V_MSG_INFO,
936
"Warning: your password will expire in %d day(s)",
943
# endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */
945
/* restrict_nologin */
946
# ifndef _PATH_NOLOGIN
947
# define _PATH_NOLOGIN "/etc/nologin"
951
# ifdef HAVE_SETUSERCONTEXT
952
/* Do we ignore a nologin file? */
953
!login_getcapbool(lc, "ignorenologin", False)) &&
954
(!stat((nolg = login_getcapstr(lc, "nologin", "", 0)), &st) ||
956
!stat((nolg = _PATH_NOLOGIN), &st)))
958
if (st.st_size && (fd = open(nolg, O_RDONLY)) >= 0) {
959
if ((buf = Malloc(st.st_size + 1))) {
960
if (read(fd, buf, st.st_size) == st.st_size) {
963
displayStr(V_MSG_ERR, buf);
972
displayStr(V_MSG_ERR,
973
"Logins are not allowed at the moment.\nTry again later");
979
# if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK)
980
if (!auth_timeok(lc, time(0))) {
981
displayStr(V_MSG_ERR,
982
"You are not allowed to login at the moment");
988
# ifdef HAVE_GETUSERSHELL
990
if (!(s = getusershell())) {
991
debug("shell not in /etc/shells\n");
993
V_RET_FAIL("Your login shell is not listed in /etc/shells");
995
if (!strcmp(s, p->pw_shell)) {
1002
# endif /* !USE_PAM */
1004
/* restrict_nohome */
1005
# ifdef HAVE_SETUSERCONTEXT
1006
if (login_getcapbool(lc, "requirehome", False)) {
1008
if (!*p->pw_dir || stat(p->pw_dir, &st) || st.st_uid != p->pw_uid) {
1009
displayStr(V_MSG_ERR, "Home folder not available");
1023
static const char *envvars[] = {
1024
"TZ", /* SYSV and SVR4, but never hurts */
1026
"AUTHSTATE", /* for kerberos */
1032
#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1033
static int num_saved_gids;
1034
static gid_t *saved_gids;
1039
num_saved_gids = getgroups(0, 0);
1040
if (!(saved_gids = Malloc(sizeof(gid_t) * num_saved_gids)))
1042
if (getgroups(num_saved_gids, saved_gids) < 0) {
1043
logError("saving groups failed: %m\n");
1052
if (setgroups(num_saved_gids, saved_gids) < 0) {
1053
logError("restoring groups failed: %m\n");
1056
if (setgid(p->pw_gid) < 0) {
1057
logError("restoring gid failed: %m\n");
1062
#endif /* USE_PAM && HAVE_INITGROUPS */
1067
#ifdef HAVE_INITGROUPS
1068
if (setgroups(0, &p->pw_gid /* anything */) < 0) {
1069
logError("restoring groups failed: %m\n");
1073
if (setgid(0) < 0) {
1074
logError("restoring gid failed: %m\n");
1081
setGid(const char *name, int gid)
1083
if (setgid(gid) < 0) {
1084
logError("setgid(%d) (user %s) failed: %m\n", gid, name);
1087
#ifdef HAVE_INITGROUPS
1088
if (initgroups(name, gid) < 0) {
1089
logError("initgroups for %s failed: %m\n", name);
1093
#endif /* QNX4 doesn't support multi-groups, no initgroups() */
1098
setUid(const char *name, int uid)
1100
if (setuid(uid) < 0) {
1101
logError("setuid(%d) (user %s) failed: %m\n", uid, name);
1108
setUser(const char *name, int uid, int gid)
1110
if (setGid(name, gid)) {
1111
if (setUid(name, uid))
1119
changeUser(const char *user, const char *authfile)
1128
if (!(pw = getpwnam(user))) {
1129
uid = strtol(user, &ok, 10);
1130
if (*ok || !(pw = getpwuid(uid))) {
1131
logError("no user like %'s\n", user);
1136
if (authfile && chown(authfile, pw->pw_uid, pw->pw_gid))
1137
logWarn("chmod for %s failed: %m\n", authfile);
1140
if (setpcred(user, NULL)) {
1141
logError("setusercontext for %s failed: %m\n", user);
1145
#elif defined(HAS_SETUSERCONTEXT)
1146
if (setusercontext(NULL, pw, pw->pw_uid, LOGIN_SETALL)) {
1147
logError("setpcred for %s failed: %m\n", user);
1152
return setUser(user, pw->pw_uid, pw->pw_gid);
1156
#if defined(SECURE_RPC) || defined(K5AUTH)
1158
nukeAuth(int len, const char *name)
1162
for (i = 0; i < td->authNum; i++)
1163
if (td->authorizations[i]->name_length == len &&
1164
!memcmp(td->authorizations[i]->name, name, len))
1166
memcpy(&td->authorizations[i], &td->authorizations[i+1],
1167
sizeof(td->authorizations[i]) * (--td->authNum - i));
1174
mergeSessionArgs(int cansave)
1182
if ((!curdmrc || newdmrc) && *dmrcDir)
1183
if (strApp(&mfname, dmrcDir, "/", curuser, fname, (char *)0))
1187
curdmrc = iniLoad(fname);
1189
strDup(&curdmrc, "[Desktop]\nSession=default\n");
1194
curdmrc = iniMerge(curdmrc, newdmrc);
1197
if (needsave && cansave)
1198
if (!iniSave(curdmrc, fname) && errno == ENOENT && mfname) {
1199
for (i = 0; mfname[i]; i++)
1200
if (mfname[i] == '/') {
1202
mkdir(mfname, 0755);
1205
iniSave(curdmrc, mfname);
1211
createClientLog(const char *log)
1213
char randstr[32], *randstrp = 0, *lname;
1217
struct expando macros[] = {
1218
{ 'd', 0, td->name },
1219
{ 'u', 0, curuser },
1220
{ 'r', 0, randstrp },
1223
if (!(lname = expandMacros(log, macros)))
1226
if ((lfd = open(lname, O_WRONLY|O_CREAT|O_EXCL, 0600)) >= 0) {
1233
if (errno != EEXIST || !macros[2].uses) {
1237
logInfo("Session log file %s not usable, trying another one.\n",
1240
sprintf(randstr, "%d", secureRandom());
1245
static int removeAuth;
1247
static int removeSession;
1248
static int removeCreds;
1250
#ifdef HAVE_CKCONNECTOR
1251
static CkConnector *ckConnector;
1254
static GPipe ctlpipe;
1255
static GTalk ctltalk;
1258
sendStr(int lv, const char *msg)
1268
sendMsg(int lv, const char *msg, ...)
1273
va_start(args, msg);
1274
VASPrintf(&ae, msg, args);
1284
startClient(volatile int *pid)
1286
const char *home, *sessargs, *desksess;
1288
char **argv, *fname, *str;
1290
char ** volatile pam_env;
1291
# ifndef HAVE_PAM_GETENVLIST
1299
extern char **newenv; /* from libs.a, this is set up by setpenv */
1302
#ifdef HAVE_CKCONNECTOR
1306
char *remoteHostName = 0;
1307
const char *spaceStr = "";
1309
char ckDeviceBuf[20] = "";
1310
const char *ckDevice = ckDeviceBuf;
1311
dbus_bool_t isLocal;
1313
char *failsafeArgv[2];
1317
if (strCmp(dmrcuser, curuser)) {
1320
curdmrc = dmrcuser = 0;
1323
#if defined(USE_PAM) || defined(_AIX)
1324
if (!(p = getpwnam(curuser))) {
1325
logError("getpwnam(%s) failed.\n", curuser);
1327
displayStr(V_MSG_ERR, 0);
1332
strcpy(curuser, p->pw_name); /* Use normalized login name. */
1334
#ifdef HAVE_CKCONNECTOR
1335
if (!(ckConnector = ck_connector_new())) {
1341
if (td->serverVT > 0)
1342
sprintf(ckDeviceBuf, "/dev/tty%d", td->serverVT);
1344
isLocal = ((td->displayType & d_location) == dLocal);
1351
family = convertAddr((char *)td->peer.data, &length, &data);
1352
addr.data = (unsigned char *)data;
1353
addr.length = length;
1355
/* We are not simply using the addr part of td->name as it might be
1356
numeric due to the SourceAddress settings */
1357
remoteHostName = networkAddressToHostname(family, &addr);
1361
dbus_error_init(&error);
1362
ckStatus = ck_connector_open_session_with_parameters(ckConnector, &error,
1363
"unix-user", &p->pw_uid,
1364
"x11-display-device", &ckDevice,
1365
"x11-display", &td->name,
1366
"is-local", &isLocal, /* meaning not entirely clear per doc */
1368
"remote-host-name", remoteHostName ?
1369
(const char **)&remoteHostName : &spaceStr,
1373
free(remoteHostName);
1375
debug("ck status: %d\n", ckStatus);
1377
if (dbus_error_is_set(&error)) {
1378
logError("Cannot open ConsoleKit session: %s\n", error.message);
1379
displayMsg(V_MSG_ERR,
1380
"Warning: Cannot open ConsoleKit session: %s",
1382
dbus_error_free(&error);
1384
logError("Cannot open ConsoleKit session, likely OOM\n");
1385
displayStr(V_MSG_ERR,
1386
"Warning: Cannot open ConsoleKit session.");
1388
ck_connector_unref(ckConnector);
1396
loginsuccess(curuser, hostname, tty, &msg);
1398
debug("loginsuccess() - %s\n", msg);
1402
# if defined(KERBEROS) && defined(AFS)
1403
if (krbtkfile[0] != '\0') {
1406
if (k_setpag() == -1) {
1407
logError("setpag() for %s failed\n", curuser);
1410
if ((ret = k_afsklog(0, 0)) != KSUCCESS) {
1411
logError("AFS Warning: %s\n", krb_get_err_text(ret));
1415
displayMsg(V_MSG_ERR,
1416
"Warning: Problems during Kerberos4/AFS setup.");
1419
# endif /* KERBEROS && AFS */
1426
env = baseEnv(0, curuser);
1428
strApp(&xma, "method=", curtype, (char *)0);
1430
strApp(&xma, ",auto", (char *)0);
1432
env = setEnv(env, "XDM_MANAGED", xma);
1435
if (td->autoLock && cursource == PWSRC_AUTOLOGIN)
1436
env = setEnv(env, "DESKTOP_LOCKED", "true");
1437
env = setEnv(env, "PATH", curuid ? td->userPath : td->systemPath);
1438
env = setEnv(env, "SHELL", p->pw_shell);
1439
env = setEnv(env, "HOME", p->pw_dir);
1440
#if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS)
1441
if (krbtkfile[0] != '\0')
1442
env = setEnv(env, "KRBTKFILE", krbtkfile);
1444
#ifdef HAVE_CKCONNECTOR
1445
env = setEnv(env, "XDG_SESSION_COOKIE",
1446
ck_connector_get_cookie(ckConnector));
1448
userEnviron = inheritEnv(env, envvars);
1449
env = systemEnv(0, curuser);
1450
systemEnviron = setEnv(env, "HOME", p->pw_dir);
1451
debug("user environment:\n%[|''>'\n's"
1452
"system environment:\n%[|''>'\n's"
1453
"end of environments\n",
1458
* for user-based authorization schemes,
1459
* add the user to the server's allowed "hosts" list.
1461
for (i = 0; i < td->authNum; i++) {
1463
if (td->authorizations[i]->name_length == 9 &&
1464
!memcmp(td->authorizations[i]->name, "SUN-DES-1", 9))
1467
char netname[MAXNETNAMELEN+1];
1468
char domainname[MAXNETNAMELEN+1];
1470
getdomainname(domainname, sizeof(domainname));
1471
user2netname(netname, curuid, domainname);
1472
addr.family = FamilyNetname;
1473
addr.length = strlen(netname);
1474
addr.address = netname;
1475
XAddHost(dpy, &addr);
1479
if (td->authorizations[i]->name_length == 14 &&
1480
!memcmp(td->authorizations[i]->name, "MIT-KERBEROS-5", 14))
1482
/* Update server's auth file with user-specific info.
1483
* Don't need to AddHost because X server will do that
1484
* automatically when it reads the cache we are about
1487
XauDisposeAuth(td->authorizations[i]);
1488
td->authorizations[i] =
1489
krb5GetAuthFor(14, "MIT-KERBEROS-5", td->name);
1490
saveServerAuthorizations(td, td->authorizations, td->authNum);
1496
mergeSessionArgs(True);
1498
debug("now starting the session\n");
1502
# ifdef HAVE_SETUSERCONTEXT
1503
if (setusercontext(lc, p, p->pw_uid, LOGIN_SETGROUP)) {
1504
logError("setusercontext(groups) for %s failed: %m\n",
1509
if (!setGid(curuser, curgid))
1513
# ifndef HAVE_PAM_GETENVLIST
1514
if (!(pam_env = initStrArr(0))) {
1518
saved_env = environ;
1521
removeCreds = True; /* set it first - i don't trust PAM's rollback */
1522
pretc = pam_setcred(pamh, 0);
1524
# ifndef HAVE_PAM_GETENVLIST
1526
environ = saved_env;
1528
# ifdef HAVE_INITGROUPS
1529
/* This seems to be a strange place for it, but do it:
1530
- after the initial groups are set
1531
- after pam_setcred might have set something, even in the error case
1532
- before pam_setcred(DELETE_CRED) might need it
1537
if (pretc != PAM_SUCCESS) {
1538
logError("pam_setcred() for %s failed: %s\n",
1539
curuser, pam_strerror(pamh, pretc));
1544
removeSession = True; /* set it first - same as above */
1545
pretc = pam_open_session(pamh, 0);
1547
if (pretc != PAM_SUCCESS) {
1548
logError("pam_open_session() for %s failed: %s\n",
1549
curuser, pam_strerror(pamh, pretc));
1554
/* we don't want sessreg and the startup/reset scripts run with user
1555
credentials. unfortunately, we can reset only the gids. */
1558
# define D_LOGIN_SETGROUP LOGIN_SETGROUP
1560
# define D_LOGIN_SETGROUP 0
1561
#endif /* USE_PAM */
1564
chownCtrl(&td->ctrl, curuid);
1565
ctltalk.pipe = &ctlpipe;
1566
ASPrintf(&buf, "sub-daemon for display %s", td->name);
1567
ASPrintf(&buf2, "client for display %s", td->name);
1568
switch (gFork(&ctlpipe, buf, buf2, 0, 0, mstrtalk.pipe, pid)) {
1571
gCloseOnExec(ctltalk.pipe);
1572
if (Setjmp(ctltalk.errjmp))
1575
gCloseOnExec(mstrtalk.pipe);
1576
if (Setjmp(mstrtalk.errjmp))
1580
setproctitle("%s'", td->name);
1582
strApp(&prog, " '", (char *)0);
1586
Signal(SIGINT, SIG_DFL);
1588
sessreg(td, getpid(), curuser, curuid);
1590
/* We do this here, as we want to have the session as parent. */
1591
switch (source(systemEnviron, td->startup, td_setup)) {
1594
case wcCompose(0, 0, 127):
1596
default: /* Explicit failure => message already displayed. */
1597
logError("Startup script returned non-zero exit code\n");
1601
/* Memory leaks are ok here as we exec() soon. */
1603
#if defined(USE_PAM) || !defined(_AIX)
1606
/* pass in environment variables set by libpam and modules it called */
1607
# ifdef HAVE_PAM_GETENVLIST
1608
pam_env = pam_getenvlist(pamh);
1612
for (; *pam_env; pam_env++)
1613
userEnviron = putEnv(*pam_env, userEnviron);
1616
# ifdef HAVE_SETLOGIN
1617
if (setlogin(curuser) < 0) {
1618
logError("setlogin for %s failed: %m\n", curuser);
1621
# define D_LOGIN_SETLOGIN LOGIN_SETLOGIN
1623
# define D_LOGIN_SETLOGIN 0
1626
# if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1631
# ifndef HAVE_SETUSERCONTEXT
1634
if (!setUid(curuser, curuid))
1637
if (!setUser(curuser, curuid, curgid))
1641
# else /* !HAVE_SETUSERCONTEXT */
1644
* Destroy environment.
1645
* We need to do this before setusercontext() because that may
1646
* set or reset some environment variables.
1648
if (!(environ = initStrArr(0)))
1652
* Set the user's credentials: uid, gid, groups,
1653
* environment variables, resource limits, and umask.
1655
if (setusercontext(lc, p, p->pw_uid,
1656
LOGIN_SETALL & ~(D_LOGIN_SETGROUP|D_LOGIN_SETLOGIN)) < 0) {
1657
logError("setusercontext for %s failed: %m\n", curuser);
1661
for (i = 0; environ[i]; i++)
1662
userEnviron = putEnv(environ[i], userEnviron);
1664
# endif /* !HAVE_SETUSERCONTEXT */
1666
#else /* PAM || !_AIX */
1668
* Set the user's credentials: uid, gid, groups,
1669
* audit classes, user limits, and umask.
1671
if (setpcred(curuser, 0) == -1) {
1672
logError("setpcred for %s failed: %m\n", curuser);
1677
* Set the users process environment. Store protected variables and
1678
* obtain updated user environment list. This call will initialize
1681
userEnviron = xCopyStrArr(1, userEnviron);
1682
userEnviron[0] = (char *)"USRENVIRON:";
1683
if (setpenv(curuser, PENV_INIT | PENV_ARGV | PENV_NOEXEC,
1684
userEnviron, 0) != 0) {
1685
logError("Cannot set %s's process environment\n", curuser);
1688
userEnviron = newenv;
1693
* for user-based authorization schemes,
1694
* use the password to get the user's credentials.
1697
/* do like "keylogin" program */
1699
logInfo("No password for NIS provided.\n");
1701
char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1];
1702
int nameret, keyret;
1704
int key_set_ok = False;
1705
struct key_netstarg netst;
1707
nameret = getnetname(netname);
1708
debug("user netname: %s\n", netname);
1709
len = strlen(curpass);
1711
bzero(curpass + 8, len - 8);
1712
keyret = getsecretkey(netname, secretkey, curpass);
1713
debug("getsecretkey returns %d, key length %d\n",
1714
keyret, strlen(secretkey));
1715
netst.st_netname = netname;
1716
memcpy(netst.st_priv_key, secretkey, HEXKEYBYTES);
1717
memset(netst.st_pub_key, 0, HEXKEYBYTES);
1718
if (key_setnet(&netst) < 0)
1719
debug("Could not set secret key.\n");
1720
/* is there a key, and do we have the right password? */
1723
keyret = key_setsecret(secretkey);
1724
debug("key_setsecret returns %d\n", keyret);
1726
logError("Failed to set NIS secret key\n");
1730
/* found a key, but couldn't interpret it */
1731
logError("Password incorrect for NIS principal %s\n",
1732
nameret ? netname : curuser);
1736
nukeAuth(9, "SUN-DES-1");
1737
bzero(secretkey, strlen(secretkey));
1741
/* do like "kinit" program */
1743
logInfo("No password for Kerberos5 provided.\n");
1745
if ((str = krb5Init(curuser, curpass, td->name)))
1746
userEnviron = setEnv(userEnviron, "KRB5CCNAME", str);
1748
nukeAuth(14, "MIT-KERBEROS-5");
1750
if (td->autoReLogin) {
1752
gSendInt(D_ReLogin);
1758
bzero(curpass, strlen(curpass));
1759
setUserAuthorization(td);
1760
home = getEnv(userEnviron, "HOME");
1761
if (home && chdir(home) < 0) {
1762
logError("Cannot chdir to %s's home %s: %m\n", curuser, home);
1763
sendStr(V_MSG_ERR, "Cannot enter home directory. Using /.\n");
1765
userEnviron = setEnv(userEnviron, "HOME", "/");
1768
if (home || td->clientLogFile[0] == '/') {
1769
if (!createClientLog(td->clientLogFile)) {
1770
logWarn("Session log file according to %s cannot be created: %m\n",
1776
if (!createClientLog(td->clientLogFallback))
1777
logError("Fallback session log file according to %s cannot be created: %m\n",
1778
td->clientLogFallback);
1779
/* Could inform the user, but I guess this is only confusing. */
1782
mergeSessionArgs(home != 0);
1783
if (!(desksess = iniEntry(curdmrc, "Desktop", "Session", 0)))
1784
desksess = "failsafe"; /* only due to OOM */
1790
close(mstrtalk.pipe->fd.w);
1791
userEnviron = setEnv(userEnviron, "DESKTOP_SESSION", desksess);
1792
for (i = 0; td->sessionsDirs[i]; i++) {
1794
if (strApp(&fname, td->sessionsDirs[i], "/", desksess, ".desktop", (char *)0)) {
1795
if ((str = iniLoad(fname))) {
1796
if (!strCmp(iniEntry(str, "Desktop Entry", "Hidden", 0), "true") ||
1797
!(sessargs = iniEntry(str, "Desktop Entry", "Exec", 0)))
1806
if (!strcmp(desksess, "failsafe") ||
1807
!strcmp(desksess, "default") ||
1808
!strcmp(desksess, "custom"))
1809
sessargs = desksess;
1813
if (!(argv = parseArgs((char **)0, td->session)) ||
1814
!(argv = addStrArr(argv, sessargs, -1)))
1816
if (argv[0] && *argv[0]) {
1817
debug("executing session %\"[s\n", argv);
1818
execute(argv, userEnviron);
1819
logError("Session %\"s execution failed: %m\n", argv[0]);
1821
logError("Session has no command/arguments\n");
1823
failsafeArgv[0] = td->failsafeClient;
1824
failsafeArgv[1] = 0;
1825
execute(failsafeArgv, userEnviron);
1826
logError("Failsafe client %\"s execution failed: %m\n",
1829
sendStr(V_MSG_ERR, 0);
1835
debug("StartSession, fork succeeded %d\n", *pid);
1842
if (!Setjmp(ctltalk.errjmp))
1843
while (gRecvCmd(&i)) {
1850
gClosen(ctltalk.pipe);
1863
#ifdef HAVE_CKCONNECTOR
1868
switch (source(systemEnviron, td->reset, td_setup)) {
1870
case wcCompose(0, 0, 127):
1873
logError("Reset script returned non-zero exit code\n");
1876
sessreg(td, 0, 0, 0);
1878
switch (Fork(&pid)) {
1880
#if defined(USE_PAM) && defined(HAVE_INITGROUPS)
1881
if (restoreGids() && setUid(curuser, curuid))
1883
if (setUser(curuser, curuid, curgid))
1887
removeUserAuthorization(td);
1889
krb5Destroy(td->name);
1891
#if !defined(USE_PAM) && !defined(_AIX)
1901
#endif /* !USE_PAM && !_AIX*/
1905
logError("Cannot clean up session: fork() failed: %m");
1916
# ifdef HAVE_INITGROUPS
1919
if (removeSession) {
1920
pretc = pam_close_session(pamh, 0);
1922
if (pretc != PAM_SUCCESS)
1923
logError("pam_close_session() failed: %s\n",
1924
pam_strerror(pamh, pretc));
1926
pretc = pam_setcred(pamh, PAM_DELETE_CRED);
1928
if (pretc != PAM_SUCCESS)
1929
logError("pam_setcred(DELETE_CRED) failed: %s\n",
1930
pam_strerror(pamh, pretc));
1932
removeCreds = False;
1935
pam_end(pamh, PAM_SUCCESS);
1941
#ifdef HAVE_CKCONNECTOR
1943
dbus_error_init(&error);
1944
if (!ck_connector_close_session(ckConnector, &error)) {
1945
if (dbus_error_is_set(&error)) {
1946
logError("Cannot close ConsoleKit session: %s\n", error.message);
1947
dbus_error_free(&error);
1949
logError("Cannot close ConsoleKit session, likely OOM\n");
1952
ck_connector_unref(ckConnector);
1959
sessionExit(int status)
1967
/* make sure the server gets reset after the session is over */
1968
if (td->serverPid >= 2) {
1969
if (!td->terminateServer)
1970
terminateProcess(td->serverPid, SIGHUP);
1974
debug("display %s exiting with status %d\n", td->name, status);
1981
char *data, *fname = 0;
1982
int len, pid, pfd[2], err;
1985
if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam(dmrcuser)))
1989
if (!strApp(&fname, dmrcDir, "/", p->pw_name, ".dmrc", (char *)0))
1991
if (!(curdmrc = iniLoad(fname))) {
1999
if (!strApp(&fname, p->pw_dir, "/.dmrc", (char *)0))
2003
switch (Fork(&pid)) {
2009
if (!setUser(p->pw_name, p->pw_uid, p->pw_gid))
2011
if (!(data = iniLoad(fname))) {
2012
static const int m1 = -1;
2013
write(pfd[1], &m1, sizeof(int));
2017
write(pfd[1], &len, sizeof(int));
2018
write(pfd[1], data, len + 1);
2024
if (reader(pfd[0], &len, sizeof(int)) == sizeof(int)) {
2027
} else if ((curdmrc = Malloc(len + 1))) {
2028
if (reader(pfd[0], curdmrc, len + 1) == len + 1) {