void MsgSubStr( int resourceid, char *buff, char *p ) /***************************************************/ { char msgbuff[MAX_RESOURCE_SIZE]; char *src; char *dest; char ch; MsgGet( resourceid, msgbuff ); src = msgbuff; dest = buff; for(;;) { ch = *src++; if( ch == '\0' ) break; if( ch != '%' ) { *dest++ = ch; } else { ch = *src++; switch( ch ) { case 's' : dest = strApp( dest, p ); break; case 'Z' : { char tmpbuff[MAX_RESOURCE_SIZE]; MsgGet( MSG_SYS_ERR_0+errno, tmpbuff ); dest = strApp( dest, tmpbuff ); } break; default : *dest++ = ch; break; } } } *dest = '\0'; }
int main(int argc, char **argv) { int oldpid, oldumask, fd, parentPid; char *pt, *errorLogFile, **opts; /* make sure at least world write access is disabled */ if (((oldumask = umask(022)) & 002) == 002) (void)umask(oldumask); /* give /dev/null as stdin */ if ((fd = open("/dev/null", O_RDONLY)) > 0) { dup2(fd, 0); close(fd); } if (fcntl(1, F_GETFD) < 0) dup2(0, 1); if (fcntl(2, F_GETFD) < 0) dup2(0, 2); #ifndef nowMonotonic nowMonotonic = sysconf(_SC_MONOTONIC_CLOCK) >= 200112L; #endif #if KDM_LIBEXEC_STRIP == -1 prog = strrchr(argv[0], '/'); progname = prog = prog ? prog + 1 : argv[0]; #else if (argv[0][0] == '/') { if (!strDup(&progpath, argv[0])) panic("Out of memory"); } else # ifdef __linux__ { /* note that this will resolve symlinks ... */ int len; char fullpath[PATH_MAX]; if ((len = readlink("/proc/self/exe", fullpath, sizeof(fullpath))) < 0) panic("Invoke with full path specification or mount /proc"); if (!strNDup(&progpath, fullpath, len)) panic("Out of memory"); } # else # if 0 panic("Must be invoked with full path specification"); # else { char directory[PATH_MAX+1]; if (!getcwd(directory, sizeof(directory))) panic("Can't find myself (getcwd failed)"); if (strchr(argv[0], '/')) { strApp(&progpath, directory, "/", argv[0], (char *)0); } else { int len; char *path, *pathe, *name, *thenam, nambuf[PATH_MAX+1]; if (!(path = getenv("PATH"))) panic("Can't find myself (no PATH)"); len = strlen(argv[0]); name = nambuf + PATH_MAX - len; memcpy(name, argv[0], len + 1); *--name = '/'; do { if (!(pathe = strchr(path, ':'))) pathe = path + strlen(path); len = pathe - path; if (!len || (len == 1 && *path == '.')) { len = strlen(directory); path = directory; } thenam = name - len; if (thenam >= nambuf) { memcpy(thenam, path, len); if (!access(thenam, X_OK)) goto found; } path = pathe; } while (*path++ != '\0'); panic("Can't find myself (not in PATH)"); found: if (!strDup(&progpath, thenam)) panic("Out of memory"); } } # endif # endif prog = strrchr(progpath, '/') + 1; # if KDM_LIBEXEC_STRIP for (progname = pt = prog, fd = 0; fd < KDM_LIBEXEC_STRIP + 1; fd++) { for (;;) { pt--; if (pt == progpath) panic("Executable is obviously located outside BINDIR"); if (*pt == '/') break; } } *pt = 0; # endif #endif #if !defined(HAVE_SETPROCTITLE) && !defined(NOXDMTITLE) title = argv[0]; titleLen = (argv[argc - 1] + strlen(argv[argc - 1])) - title; #endif /* * Parse command line options */ parentPid = getppid(); errorLogFile = 0; if (!(opts = Malloc(2 * sizeof(char *)))) return 1; opts[0] = (char *)""; opts[1] = 0; while (*++argv) { if (**argv != '-') break; pt = *argv + 1; if (*pt == '-') pt++; if (!strcmp(pt, "help") || !strcmp(pt, "h")) { printf("Usage: %s [options] [tty]\n" " -daemon\t - Daemonize even when started by init\n" " -nodaemon\t - Do not daemonize even when started from command line\n" " -config <file> - Use alternative master configuration file\n" " -xrm <res>\t - Override frontend-specific resource\n" " -error <file>\t - Use alternative log file\n" " -debug <num>\t - Debug option bitfield:\n" "\t\t\t0x1 - core log\n" "\t\t\t0x2 - config reader log\n" "\t\t\t0x4 - greeter log\n" "\t\t\t0x8 - IPC log\n" "\t\t\t0x10 - session sub-daemon post-fork delay\n" "\t\t\t0x20 - config reader post-start delay\n" "\t\t\t0x40 - greeter post-start delay\n" "\t\t\t0x80 - do not use syslog\n" "\t\t\t0x100 - core Xauth log\n" "\t\t\t0x200 - debug greeter theming\n" "\t\t\t0x400 - valgrind config reader and greeter\n" "\t\t\t0x800 - strace config reader and greeter\n" , prog); exit(0); } else if (!strcmp(pt, "daemon")) { parentPid = 0; } else if (!strcmp(pt, "nodaemon")) { parentPid = 1; } else if (argv[1] && !strcmp(pt, "config")) { strDup(opts, *++argv); } else if (argv[1] && !strcmp(pt, "xrm")) { opts = addStrArr(opts, *++argv, -1); } else if (argv[1] && !strcmp(pt, "debug")) { sscanf(*++argv, "%i", &debugLevel); } else if (argv[1] && (!strcmp(pt, "error") || !strcmp(pt, "logfile"))) { errorLogFile = *++argv; } else { fprintf(stderr, "\"%s\" is an unknown option or is missing a parameter\n", *argv); exit(1); } } /* * Only allow root to run in non-debug mode to avoid problems */ if (!debugLevel && getuid()) { fprintf(stderr, "Only root wants to run %s\n", prog); exit(1); } initErrorLog(errorLogFile); if (parentPid != 1) becomeDaemon(); /* * Step 1 - load configuration parameters */ if (!initResources(opts) || scanConfigs(False) < 0) logPanic("Config reader failed. Aborting ...\n"); /* SUPPRESS 560 */ if ((oldpid = storePid())) { if (oldpid == -1) logError("Cannot create/lock pid file %s\n", pidFile); else logError("Cannot lock pid file %s, another xdm is running (pid %d)\n", pidFile, oldpid); exit(1); } #ifdef NEED_ENTROPY addOtherEntropy(); #endif /* * We used to clean up old authorization files here. As authDir is * supposed to be /var/run/xauth or /tmp, we needn't to care for it. */ #ifdef XDMCP initXdmcp(); #else debug("not compiled for XDMCP\n"); #endif if (pipe(signalFds)) logPanic("Unable to create signal notification pipe.\n"); registerInput(signalFds[0]); registerCloseOnFork(signalFds[0]); registerCloseOnFork(signalFds[1]); (void)Signal(SIGTERM, sigHandler); (void)Signal(SIGINT, sigHandler); (void)Signal(SIGHUP, sigHandler); (void)Signal(SIGCHLD, sigHandler); (void)Signal(SIGUSR1, sigHandler); /* * Step 2 - run a sub-daemon for each entry */ openCtrl(0); #ifdef XDMCP updateListenSockets(); #endif mainLoop(); closeCtrl(0); if (sdRec.how) { int pid; commitBootOption(); if (Fork(&pid) <= 0) { char *cmd = sdRec.how == SHUT_HALT ? cmdHalt : cmdReboot; execute(parseArgs((char **)0, cmd), (char **)0); logError("Failed to execute shutdown command %\"s\n", cmd); exit(1); } else { sigset_t mask; sigemptyset(&mask); sigaddset(&mask, SIGCHLD); sigaddset(&mask, SIGHUP); sigsuspend(&mask); } } debug("nothing left to do, exiting\n"); return 0; }
void openCtrl(struct display *d) { CtrlRec *cr; const char *dname; char *sockdir; struct sockaddr_un sa; if (!*fifoDir) return; if (d) cr = &d->ctrl, dname = displayName(d); else cr = &ctrl, dname = 0; if (cr->fd < 0) { if (mkdir(fifoDir, 0755)) { if (errno != EEXIST) { logError("mkdir %\"s failed: %m; no control sockets will be available\n", fifoDir); return; } } else { chmod(fifoDir, 0755); /* override umask */ } sockdir = 0; strApp(&sockdir, fifoDir, dname ? "/dmctl-" : "/dmctl", dname, (char *)0); if (sockdir) { strApp(&cr->path, sockdir, "/socket", (char *)0); if (cr->path) { if (strlen(cr->path) >= sizeof(sa.sun_path)) { logError("path %\"s too long; control socket will not be available\n", cr->path); #ifdef HONORS_SOCKET_PERMS } else if (mkdir(sockdir, 0700) && errno != EEXIST) { logError("mkdir %\"s failed: %m; control socket will not be available\n", sockdir); } else if (unlink(cr->path) && errno != ENOENT) { logError("unlink %\"s failed: %m; control socket will not be available\n", cr->path); } else { #else } else if (unlink(sockdir) && errno != ENOENT) { logError("unlink %\"s failed: %m; control socket will not be available\n", sockdir); } else if (!strApp(&cr->realdir, sockdir, "-XXXXXX", (char *)0)) { } else if (!mkTempDir(cr->realdir)) { logError("mkdir %\"s failed: %m; control socket will not be available\n", cr->realdir); free(cr->realdir); cr->realdir = 0; } else if (symlink(cr->realdir, sockdir)) { logError("symlink %\"s => %\"s failed: %m; control socket will not be available\n", sockdir, cr->realdir); rmdir(cr->realdir); free(cr->realdir); cr->realdir = 0; } else { chown(sockdir, 0, d ? 0 : fifoGroup); chmod(sockdir, 0750); #endif if ((cr->fd = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) { logError("Cannot create control socket: %m\n"); } else { sa.sun_family = AF_UNIX; strcpy(sa.sun_path, cr->path); if (!bind(cr->fd, (struct sockaddr *)&sa, sizeof(sa))) { if (!listen(cr->fd, 5)) { #ifdef HONORS_SOCKET_PERMS chmod(cr->path, 0660); if (!d) chown(cr->path, -1, fifoGroup); chmod(sockdir, 0755); #else chmod(cr->path, 0666); #endif registerCloseOnFork(cr->fd); registerInput(cr->fd); free(sockdir); return; } unlink(cr->path); logError("Cannot listen on control socket %\"s: %m\n", cr->path); } else { logError("Cannot bind control socket %\"s: %m\n", cr->path); } close(cr->fd); cr->fd = -1; } #ifdef HONORS_SOCKET_PERMS rmdir(sockdir); #else unlink(sockdir); rmdir(cr->realdir); free(cr->realdir); cr->realdir = 0; #endif } free(cr->path); cr->path = 0; } free(sockdir); } }
static int #if defined(USE_PAM) || defined(_AIX) isNoPassAllowed( const char *un ) { struct passwd *pw = 0; # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */ struct spwd *spw; # endif #else isNoPassAllowed( const char *un, struct passwd *pw ) { #endif struct group *gr; char **fp; int hg; if (!*un) return 0; if (cursource != PWSRC_MANUAL) return 1; for (hg = 0, fp = td->noPassUsers; *fp; fp++) if (**fp == '@') hg = 1; else if (!strcmp( un, *fp )) return 1; else if (!strcmp( "*", *fp )) { #if defined(USE_PAM) || defined(_AIX) if (!(pw = getpwnam( un ))) return 0; if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') continue; # ifdef HAVE_GETSPNAM /* (sic!) - not USESHADOW */ if ((spw = getspnam( un )) && (spw->sp_pwdp[0] == '!' || spw->sp_pwdp[0] == '*')) continue; # endif #endif if (pw->pw_uid) return 1; } #if defined(USE_PAM) || defined(_AIX) if (hg && (pw || (pw = getpwnam( un )))) { #else if (hg) { #endif for (setgrent(); (gr = getgrent()); ) for (fp = td->noPassUsers; *fp; fp++) if (**fp == '@' && !strcmp( gr->gr_name, *fp + 1 )) { if (pw->pw_gid == gr->gr_gid) { endgrent(); return 1; } for (; *gr->gr_mem; gr->gr_mem++) if (!strcmp( un, *gr->gr_mem )) { endgrent(); return 1; } } endgrent(); } return 0; } #if !defined(USE_PAM) && !defined(_AIX) && defined(HAVE_SETUSERCONTEXT) # define LC_RET0 do { login_close(lc); return 0; } while(0) #else # define LC_RET0 return 0 #endif int verify( GConvFunc gconv, int rootok ) { #ifdef USE_PAM const char *psrv; struct pam_data pdata; int pretc, pnopass; char psrvb[64]; #elif defined(_AIX) char *msg, *curret; int i, reenter; #else struct stat st; const char *nolg; char *buf; int fd; # ifdef HAVE_GETUSERSHELL char *s; # endif # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) int tim, expir, warntime, quietlog; # endif #endif debug( "verify ...\n" ); #ifdef USE_PAM pnopass = FALSE; if (!strcmp( curtype, "classic" )) { if (!gconv( GCONV_USER, 0 )) return 0; if (isNoPassAllowed( curuser )) { gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { pnopass = TRUE; sprintf( psrvb, "%.31s-np", PAMService ); psrv = psrvb; } else psrv = PAMService; } else psrv = PAMService; pdata.usecur = TRUE; } else { sprintf( psrvb, "%.31s-%.31s", PAMService, curtype ); psrv = psrvb; pdata.usecur = FALSE; } pdata.gconv = gconv; if (!doPAMAuth( psrv, &pdata )) return 0; #elif defined(_AIX) if ((td->displayType & d_location) == dForeign) { char *tmpch; strncpy( hostname, td->name, sizeof(hostname) - 1 ); hostname[sizeof(hostname)-1] = '\0'; if ((tmpch = strchr( hostname, ':' ))) *tmpch = '\0'; } else hostname[0] = '\0'; /* tty names should only be 15 characters long */ # if 0 for (i = 0; i < 15 && td->name[i]; i++) { if (td->name[i] == ':' || td->name[i] == '.') tty[i] = '_'; else tty[i] = td->name[i]; } tty[i] = '\0'; # else memcpy( tty, "/dev/xdm/", 9 ); for (i = 0; i < 6 && td->name[i]; i++) { if (td->name[i] == ':' || td->name[i] == '.') tty[9 + i] = '_'; else tty[9 + i] = td->name[i]; } tty[9 + i] = '\0'; # endif if (!strcmp( curtype, "classic" )) { if (!gconv( GCONV_USER, 0 )) return 0; if (isNoPassAllowed( curuser )) { gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { debug( "accepting despite empty password\n" ); goto done; } } else if (!gconv( GCONV_PASS, 0 )) return 0; enduserdb(); msg = NULL; if ((i = authenticate( curuser, curpass, &reenter, &msg ))) { debug( "authenticate() failed: %s\n", msg ); if (msg) free( msg ); loginfailed( curuser, hostname, tty ); if (i == ENOENT || i == ESAD) V_RET_AUTH; else V_RET_FAIL( 0 ); } if (reenter) { logError( "authenticate() requests more data: %s\n", msg ); free( msg ); V_RET_FAIL( 0 ); } } else if (!strcmp( curtype, "generic" )) { if (!gconv( GCONV_USER, 0 )) return 0; for (curret = 0;;) { msg = NULL; if ((i = authenticate( curuser, curret, &reenter, &msg ))) { debug( "authenticate() failed: %s\n", msg ); if (msg) free( msg ); loginfailed( curuser, hostname, tty ); if (i == ENOENT || i == ESAD) V_RET_AUTH; else V_RET_FAIL( 0 ); } if (curret) free( curret ); if (!reenter) break; if (!(curret = gconv( GCONV_HIDDEN, msg ))) return 0; free( msg ); } } else { logError( "Unsupported authentication type %\"s requested\n", curtype ); V_RET_FAIL( 0 ); } if (msg) { displayStr( V_MSG_INFO, msg ); free( msg ); } done: #else if (strcmp( curtype, "classic" )) { logError( "Unsupported authentication type %\"s requested\n", curtype ); V_RET_FAIL( 0 ); } if (!gconv( GCONV_USER, 0 )) return 0; if (!(p = getpwnam( curuser ))) { debug( "getpwnam() failed.\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { debug( "account is locked\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } # ifdef USESHADOW if ((sp = getspnam( curuser ))) { p->pw_passwd = sp->sp_pwdp; if (p->pw_passwd[0] == '!' || p->pw_passwd[0] == '*') { debug( "account is locked\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } } else debug( "getspnam() failed: %m. Are you root?\n" ); # endif if (!*p->pw_passwd) { if (!td->allowNullPasswd) { debug( "denying user with empty password\n" ); gconv( GCONV_PASS, 0 ); V_RET_AUTH; } goto nplogin; } if (isNoPassAllowed( curuser, p )) { nplogin: gconv( GCONV_PASS_ND, 0 ); if (!*curpass) { debug( "accepting password-less login\n" ); goto done; } } else if (!gconv( GCONV_PASS, 0 )) return 0; # ifdef KERBEROS if (p->pw_uid) { int ret; char realm[REALM_SZ]; if (krb_get_lrealm( realm, 1 )) { logError( "Cannot get KerberosIV realm.\n" ); V_RET_FAIL( 0 ); } sprintf( krbtkfile, "%s.%.*s", TKT_ROOT, MAXPATHLEN - strlen( TKT_ROOT ) - 2, td->name ); krb_set_tkt_string( krbtkfile ); unlink( krbtkfile ); ret = krb_verify_user( curuser, "", realm, curpass, 1, "rcmd" ); if (ret == KSUCCESS) { chown( krbtkfile, p->pw_uid, p->pw_gid ); debug( "KerberosIV verify succeeded\n" ); goto done; } else if (ret != KDC_PR_UNKNOWN && ret != SKDC_CANT) { logError( "KerberosIV verification failure %\"s for %s\n", krb_get_err_text( ret ), curuser ); krbtkfile[0] = '\0'; V_RET_FAIL( 0 ); } debug( "KerberosIV verify failed: %s\n", krb_get_err_text( ret ) ); } krbtkfile[0] = '\0'; # endif /* KERBEROS */ # if defined(ultrix) || defined(__ultrix__) if (authenticate_user( p, curpass, NULL ) < 0) # elif defined(HAVE_PW_ENCRYPT) if (strcmp( pw_encrypt( curpass, p->pw_passwd ), p->pw_passwd )) # elif defined(HAVE_CRYPT) if (strcmp( crypt( curpass, p->pw_passwd ), p->pw_passwd )) # else if (strcmp( curpass, p->pw_passwd )) # endif { debug( "password verify failed\n" ); V_RET_AUTH; } done: #endif /* !defined(USE_PAM) && !defined(_AIX) */ debug( "restrict %s ...\n", curuser ); #if defined(USE_PAM) || defined(_AIX) if (!(p = getpwnam( curuser ))) { logError( "getpwnam(%s) failed.\n", curuser ); V_RET_FAIL( 0 ); } #endif if (!p->pw_uid) { if (!rootok && !td->allowRootLogin) V_RET_FAIL( "Root logins are not allowed" ); return 1; /* don't deny root to log in */ } #ifdef USE_PAM debug( " pam_acct_mgmt() ...\n" ); pretc = pam_acct_mgmt( pamh, 0 ); reInitErrorLog(); debug( " pam_acct_mgmt() returned: %s\n", pam_strerror( pamh, pretc ) ); if (pretc == PAM_NEW_AUTHTOK_REQD) { pdata.usecur = FALSE; pdata.gconv = conv_interact; /* pam will have output a message already, so no prepareErrorGreet() */ if (gconv != conv_interact || pnopass) { pam_end( pamh, PAM_SUCCESS ); pamh = 0; gSendInt( V_CHTOK_AUTH ); /* this cannot auth the wrong user, as only classic auths get here */ while (!doPAMAuth( PAMService, &pdata )) if (pdata.abort) return 0; gSendInt( V_PRE_OK ); } else gSendInt( V_CHTOK ); for (;;) { debug( " pam_chauthtok() ...\n" ); pretc = pam_chauthtok( pamh, PAM_CHANGE_EXPIRED_AUTHTOK ); reInitErrorLog(); debug( " pam_chauthtok() returned: %s\n", pam_strerror( pamh, pretc ) ); if (pdata.abort) { pam_end( pamh, PAM_SUCCESS ); pamh = 0; return 0; } if (pretc == PAM_SUCCESS) break; /* effectively there is only PAM_AUTHTOK_ERR */ gSendInt( V_FAIL ); } if (curpass) free( curpass ); curpass = newpass; newpass = 0; } else if (pretc != PAM_SUCCESS) { pam_end( pamh, pretc ); pamh = 0; V_RET_AUTH; } #elif defined(_AIX) /* USE_PAM */ msg = NULL; if (loginrestrictions( curuser, ((td->displayType & d_location) == dForeign) ? S_RLOGIN : S_LOGIN, tty, &msg ) == -1) { debug( "loginrestrictions() - %s\n", msg ? msg : "error" ); loginfailed( curuser, hostname, tty ); prepareErrorGreet(); if (msg) { displayStr( V_MSG_ERR, msg ); free( msg ); } gSendInt( V_AUTH ); return 0; } if (msg) free( (void *)msg ); #endif /* USE_PAM || _AIX */ #ifndef _AIX # ifdef HAVE_SETUSERCONTEXT # ifdef HAVE_LOGIN_GETCLASS lc = login_getclass( p->pw_class ); # else lc = login_getpwclass( p ); # endif if (!lc) V_RET_FAIL( 0 ); p->pw_shell = login_getcapstr( lc, "shell", p->pw_shell, p->pw_shell ); # endif # ifndef USE_PAM /* restrict_expired */ # if defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || defined(USESHADOW) # if !defined(HAVE_STRUCT_PASSWD_PW_EXPIRE) || (!defined(HAVE_SETUSERCONTEXT) && defined(USESHADOW)) if (sp) # endif { # define DEFAULT_WARN (2L * 7L) /* Two weeks */ tim = time( NULL ) / 86400L; # ifdef HAVE_SETUSERCONTEXT quietlog = login_getcapbool( lc, "hushlogin", 0 ); warntime = login_getcaptime( lc, "warnexpire", DEFAULT_WARN * 86400L, DEFAULT_WARN * 86400L ) / 86400L; # else quietlog = 0; # ifdef USESHADOW warntime = sp->sp_warn != -1 ? sp->sp_warn : DEFAULT_WARN; # else warntime = DEFAULT_WARN; # endif # endif # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE if (p->pw_expire) { expir = p->pw_expire / 86400L; # else if (sp->sp_expire != -1) { expir = sp->sp_expire; # endif if (tim > expir) { displayStr( V_MSG_ERR, "Your account has expired;" " please contact your system administrator" ); gSendInt( V_FAIL ); LC_RET0; } else if (tim > (expir - warntime) && !quietlog) { displayMsg( V_MSG_INFO, "Warning: your account will expire in %d day(s)", expir - tim ); } } # ifdef HAVE_STRUCT_PASSWD_PW_EXPIRE if (p->pw_change) { expir = p->pw_change / 86400L; # else if (!sp->sp_lstchg) { displayStr( V_MSG_ERR, "You are required to change your password immediately" " (root enforced)" ); /* XXX todo password change */ gSendInt( V_FAIL ); LC_RET0; } else if (sp->sp_max != -1) { expir = sp->sp_lstchg + sp->sp_max; if (sp->sp_inact != -1 && tim > expir + sp->sp_inact) { displayStr( V_MSG_ERR, "Your account has expired;" " please contact your system administrator" ); gSendInt( V_FAIL ); LC_RET0; } # endif if (tim > expir) { displayStr( V_MSG_ERR, "You are required to change your password immediately" " (password aged)" ); /* XXX todo password change */ gSendInt( V_FAIL ); LC_RET0; } else if (tim > (expir - warntime) && !quietlog) { displayMsg( V_MSG_INFO, "Warning: your password will expire in %d day(s)", expir - tim ); } } } # endif /* HAVE_STRUCT_PASSWD_PW_EXPIRE || USESHADOW */ /* restrict_nologin */ # ifndef _PATH_NOLOGIN # define _PATH_NOLOGIN "/etc/nologin" # endif if (( # ifdef HAVE_SETUSERCONTEXT /* Do we ignore a nologin file? */ !login_getcapbool( lc, "ignorenologin", 0 )) && (!stat( (nolg = login_getcapstr( lc, "nologin", "", NULL )), &st ) || # endif !stat( (nolg = _PATH_NOLOGIN), &st ))) { if (st.st_size && (fd = open( nolg, O_RDONLY )) >= 0) { if ((buf = Malloc( st.st_size + 1 ))) { if (read( fd, buf, st.st_size ) == st.st_size) { close( fd ); buf[st.st_size] = 0; displayStr( V_MSG_ERR, buf ); free( buf ); gSendInt( V_FAIL ); LC_RET0; } free( buf ); } close( fd ); } displayStr( V_MSG_ERR, "Logins are not allowed at the moment.\nTry again later" ); gSendInt( V_FAIL ); LC_RET0; } /* restrict_time */ # if defined(HAVE_SETUSERCONTEXT) && defined(HAVE_AUTH_TIMEOK) if (!auth_timeok( lc, time( NULL ) )) { displayStr( V_MSG_ERR, "You are not allowed to login at the moment" ); gSendInt( V_FAIL ); LC_RET0; } # endif # ifdef HAVE_GETUSERSHELL for (;;) { if (!(s = getusershell())) { debug( "shell not in /etc/shells\n" ); endusershell(); V_RET_FAIL( "Your login shell is not listed in /etc/shells" ); } if (!strcmp( s, p->pw_shell )) { endusershell(); break; } } # endif # endif /* !USE_PAM */ /* restrict_nohome */ # ifdef HAVE_SETUSERCONTEXT if (login_getcapbool( lc, "requirehome", 0 )) { struct stat st; if (!*p->pw_dir || stat( p->pw_dir, &st ) || st.st_uid != p->pw_uid) { displayStr( V_MSG_ERR, "Home folder not available" ); gSendInt( V_FAIL ); LC_RET0; } } # endif #endif /* !_AIX */ return 1; } static const char *envvars[] = { "TZ", /* SYSV and SVR4, but never hurts */ #ifdef _AIX "AUTHSTATE", /* for kerberos */ #endif NULL }; #if defined(USE_PAM) && defined(HAVE_INITGROUPS) static int num_saved_gids; static gid_t *saved_gids; static int saveGids( void ) { num_saved_gids = getgroups( 0, 0 ); if (!(saved_gids = Malloc( sizeof(gid_t) * num_saved_gids ))) return 0; if (getgroups( num_saved_gids, saved_gids ) < 0) { logError( "saving groups failed: %m\n" ); return 0; } return 1; } static int restoreGids( void ) { if (setgroups( num_saved_gids, saved_gids ) < 0) { logError( "restoring groups failed: %m\n" ); return 0; } if (setgid( p->pw_gid ) < 0) { logError( "restoring gid failed: %m\n" ); return 0; } return 1; } #endif /* USE_PAM && HAVE_INITGROUPS */ static int resetGids( void ) { #ifdef HAVE_INITGROUPS if (setgroups( 0, &p->pw_gid /* anything */ ) < 0) { logError( "restoring groups failed: %m\n" ); return 0; } #endif if (setgid( 0 ) < 0) { logError( "restoring gid failed: %m\n" ); return 0; } return 1; } static int setGid( const char *name, int gid ) { if (setgid( gid ) < 0) { logError( "setgid(%d) (user %s) failed: %m\n", gid, name ); return 0; } #ifdef HAVE_INITGROUPS if (initgroups( name, gid ) < 0) { logError( "initgroups for %s failed: %m\n", name ); setgid( 0 ); return 0; } #endif /* QNX4 doesn't support multi-groups, no initgroups() */ return 1; } static int setUid( const char *name, int uid ) { if (setuid( uid ) < 0) { logError( "setuid(%d) (user %s) failed: %m\n", uid, name ); return 0; } return 1; } static int setUser( const char *name, int uid, int gid ) { if (setGid( name, gid )) { if (setUid( name, uid )) return 1; resetGids(); } return 0; } #if defined(SECURE_RPC) || defined(K5AUTH) static void nukeAuth( int len, const char *name ) { int i; for (i = 0; i < td->authNum; i++) if (td->authorizations[i]->name_length == len && !memcmp( td->authorizations[i]->name, name, len )) { memcpy( &td->authorizations[i], &td->authorizations[i+1], sizeof(td->authorizations[i]) * (--td->authNum - i) ); break; } } #endif static void mergeSessionArgs( int cansave ) { char *mfname; const char *fname; int i, needsave; mfname = 0; fname = ".dmrc"; if ((!curdmrc || newdmrc) && *dmrcDir) if (strApp( &mfname, dmrcDir, "/", curuser, fname, (char *)0 )) fname = mfname; needsave = 0; if (!curdmrc) { curdmrc = iniLoad( fname ); if (!curdmrc) { strDup( &curdmrc, "[Desktop]\nSession=default\n" ); needsave = 1; } } if (newdmrc) { curdmrc = iniMerge( curdmrc, newdmrc ); needsave = 1; } if (needsave && cansave) if (!iniSave( curdmrc, fname ) && errno == ENOENT && mfname) { for (i = 0; mfname[i]; i++) if (mfname[i] == '/') { mfname[i] = 0; mkdir( mfname, 0755 ); mfname[i] = '/'; } iniSave( curdmrc, mfname ); } if (mfname) free( mfname ); } static int createClientLog( const char *log ) { char randstr[32], *randstrp = 0, *lname; int lfd; for (;;) { struct expando macros[] = { { 'd', 0, td->name }, { 'u', 0, curuser }, { 'r', 0, randstrp }, { 0, 0, 0 } }; if (!(lname = expandMacros( log, macros ))) exit( 1 ); unlink( lname ); if ((lfd = open( lname, O_WRONLY|O_CREAT|O_EXCL, 0600 )) >= 0) { dup2( lfd, 1 ); dup2( lfd, 2 ); close( lfd ); free( lname ); return TRUE; } if (errno != EEXIST || !macros[2].uses) { free( lname ); return FALSE; } logInfo( "Session log file %s not usable, trying another one.\n", lname ); free( lname ); sprintf( randstr, "%d", secureRandom() ); randstrp = randstr; } }
int readDmrc() { char *data, *fname = 0; int len, pid, pfd[2], err; if (!dmrcuser || !dmrcuser[0] || !(p = getpwnam( dmrcuser ))) return GE_NoUser; if (*dmrcDir) { if (!strApp( &fname, dmrcDir, "/", dmrcuser, ".dmrc", (char *)0 )) return GE_Error; if (!(curdmrc = iniLoad( fname ))) { free( fname ); return GE_Ok; } free( fname ); return GE_NoFile; } if (!strApp( &fname, p->pw_dir, "/.dmrc", (char *)0 )) return GE_Error; if (pipe( pfd )) return GE_Error; switch (Fork( &pid )) { case -1: close( pfd[0] ); close( pfd[1] ); return GE_Error; case 0: if (!setUser( p->pw_name, p->pw_uid, p->pw_gid )) exit( 0 ); if (!(data = iniLoad( fname ))) { static const int m1 = -1; write( pfd[1], &m1, sizeof(int) ); exit( 0 ); } len = strlen( data ); write( pfd[1], &len, sizeof(int) ); write( pfd[1], data, len + 1 ); exit( 0 ); } close( pfd[1] ); free( fname ); err = GE_Error; if (reader( pfd[0], &len, sizeof(int) ) == sizeof(int)) { if (len == -1) err = GE_Denied; else if ((curdmrc = Malloc( len + 1 ))) { if (reader( pfd[0], curdmrc, len + 1 ) == len + 1) err = GE_Ok; else { free( curdmrc ); curdmrc = 0; } } } close( pfd[0] ); Wait4( &pid ); return err; }
int startClient( volatile int *pid ) { const char *home, *sessargs, *desksess; char **env, *xma; char **argv, *fname, *str; #ifdef USE_PAM char ** volatile pam_env; # ifndef HAVE_PAM_GETENVLIST char **saved_env; # endif int pretc; #else # ifdef _AIX char *msg; char **theenv; extern char **newenv; /* from libs.a, this is set up by setpenv */ # endif #endif #ifdef HAVE_SETUSERCONTEXT extern char **environ; #endif char *failsafeArgv[2]; char *buf, *buf2; int i; if (strCmp( dmrcuser, curuser )) { if (curdmrc) { free( curdmrc ); curdmrc = 0; } if (dmrcuser) { free( dmrcuser ); dmrcuser = 0; } } #if defined(USE_PAM) || defined(_AIX) if (!(p = getpwnam( curuser ))) { logError( "getpwnam(%s) failed.\n", curuser ); pError: displayStr( V_MSG_ERR, 0 ); return 0; } #endif #ifndef USE_PAM # ifdef _AIX msg = NULL; loginsuccess( curuser, hostname, tty, &msg ); if (msg) { debug( "loginsuccess() - %s\n", msg ); free( (void *)msg ); } # else /* _AIX */ # if defined(KERBEROS) && defined(AFS) if (krbtkfile[0] != '\0') { if (k_hasafs()) { int fail = 0; if (k_setpag() == -1) { logError( "setpag() for %s failed\n", curuser ); fail = 1; } if ((ret = k_afsklog( NULL, NULL )) != KSUCCESS) { logError( "AFS Warning: %s\n", krb_get_err_text( ret ) ); fail = 1; } if (fail) displayMsg( V_MSG_ERR, "Warning: Problems during Kerberos4/AFS setup." ); } } # endif /* KERBEROS && AFS */ # endif /* _AIX */ #endif /* !PAM */ curuid = p->pw_uid; curgid = p->pw_gid; env = baseEnv( curuser ); xma = 0; strApp( &xma, "method=", curtype, (char *)0 ); if (td_setup) strApp( &xma, ",auto", (char *)0 ); if (xma) { env = setEnv( env, "XDM_MANAGED", xma ); free( xma ); } if (td->autoLock && cursource == PWSRC_AUTOLOGIN) env = setEnv( env, "DESKTOP_LOCKED", "true" ); env = setEnv( env, "PATH", curuid ? td->userPath : td->systemPath ); env = setEnv( env, "SHELL", p->pw_shell ); env = setEnv( env, "HOME", p->pw_dir ); #if !defined(USE_PAM) && !defined(_AIX) && defined(KERBEROS) if (krbtkfile[0] != '\0') env = setEnv( env, "KRBTKFILE", krbtkfile ); #endif userEnviron = inheritEnv( env, envvars ); env = systemEnv( curuser ); systemEnviron = setEnv( env, "HOME", p->pw_dir ); debug( "user environment:\n%[|''>'\n's" "system environment:\n%[|''>'\n's" "end of environments\n", userEnviron, systemEnviron ); /* * for user-based authorization schemes, * add the user to the server's allowed "hosts" list. */ for (i = 0; i < td->authNum; i++) { #ifdef SECURE_RPC if (td->authorizations[i]->name_length == 9 && !memcmp( td->authorizations[i]->name, "SUN-DES-1", 9 )) { XHostAddress addr; char netname[MAXNETNAMELEN+1]; char domainname[MAXNETNAMELEN+1]; getdomainname( domainname, sizeof(domainname) ); user2netname( netname, curuid, domainname ); addr.family = FamilyNetname; addr.length = strlen( netname ); addr.address = netname; XAddHost( dpy, &addr ); } #endif #ifdef K5AUTH if (td->authorizations[i]->name_length == 14 && !memcmp( td->authorizations[i]->name, "MIT-KERBEROS-5", 14 )) { /* Update server's auth file with user-specific info. * Don't need to AddHost because X server will do that * automatically when it reads the cache we are about * to point it at. */ XauDisposeAuth( td->authorizations[i] ); td->authorizations[i] = krb5GetAuthFor( 14, "MIT-KERBEROS-5", td->name ); saveServerAuthorizations( td, td->authorizations, td->authNum ); } #endif } if (*dmrcDir) mergeSessionArgs( TRUE ); debug( "now starting the session\n" ); #ifdef USE_PAM # ifdef HAVE_SETUSERCONTEXT if (setusercontext( lc, p, p->pw_uid, LOGIN_SETGROUP )) { logError( "setusercontext(groups) for %s failed: %m\n", curuser ); goto pError; } # else if (!setGid( curuser, curgid )) goto pError; # endif # ifndef HAVE_PAM_GETENVLIST if (!(pam_env = initStrArr( 0 ))) { resetGids(); goto pError; } saved_env = environ; environ = pam_env; # endif removeCreds = 1; /* set it first - i don't trust PAM's rollback */ pretc = pam_setcred( pamh, 0 ); reInitErrorLog(); # ifndef HAVE_PAM_GETENVLIST pam_env = environ; environ = saved_env; # endif # ifdef HAVE_INITGROUPS /* This seems to be a strange place for it, but do it: - after the initial groups are set - after pam_setcred might have set something, even in the error case - before pam_setcred(DELETE_CRED) might need it */ if (!saveGids()) goto pError; # endif if (pretc != PAM_SUCCESS) { logError( "pam_setcred() for %s failed: %s\n", curuser, pam_strerror( pamh, pretc ) ); resetGids(); return 0; } removeSession = 1; /* set it first - same as above */ pretc = pam_open_session( pamh, 0 ); reInitErrorLog(); if (pretc != PAM_SUCCESS) { logError( "pam_open_session() for %s failed: %s\n", curuser, pam_strerror( pamh, pretc ) ); resetGids(); return 0; } /* we don't want sessreg and the startup/reset scripts run with user credentials. unfortunately, we can reset only the gids. */ resetGids(); # define D_LOGIN_SETGROUP LOGIN_SETGROUP #else /* USE_PAM */ # define D_LOGIN_SETGROUP 0 #endif /* USE_PAM */ removeAuth = 1; chownCtrl( &td->ctrl, curuid ); endpwent(); #if !defined(USE_PAM) && defined(USESHADOW) && !defined(_AIX) endspent(); #endif ctltalk.pipe = &ctlpipe; ASPrintf( &buf, "sub-daemon for display %s", td->name ); ASPrintf( &buf2, "client for display %s", td->name ); switch (gFork( &ctlpipe, buf, buf2, 0, 0, mstrtalk.pipe, pid )) { case 0: gCloseOnExec( ctltalk.pipe ); if (Setjmp( ctltalk.errjmp )) exit( 1 ); gCloseOnExec( mstrtalk.pipe ); if (Setjmp( mstrtalk.errjmp )) goto cError; #ifndef NOXDMTITLE setproctitle( "%s'", td->name ); #endif strApp( &prog, " '", (char *)0 ); reInitErrorLog(); setsid(); sessreg( td, getpid(), curuser, curuid ); /* We do this here, as we want to have the session as parent. */ switch (source( systemEnviron, td->startup, td_setup )) { case 0: break; case wcCompose( 0, 0, 127 ): goto cError; default: /* Explicit failure => message already displayed. */ logError( "Startup script returned non-zero exit code\n" ); exit( 1 ); } /* Memory leaks are ok here as we exec() soon. */ #if defined(USE_PAM) || !defined(_AIX) # ifdef USE_PAM /* pass in environment variables set by libpam and modules it called */ # ifdef HAVE_PAM_GETENVLIST pam_env = pam_getenvlist( pamh ); reInitErrorLog(); # endif if (pam_env) for (; *pam_env; pam_env++) userEnviron = putEnv( *pam_env, userEnviron ); # endif # ifdef HAVE_SETLOGIN if (setlogin( curuser ) < 0) { logError( "setlogin for %s failed: %m\n", curuser ); goto cError; } # define D_LOGIN_SETLOGIN LOGIN_SETLOGIN # else # define D_LOGIN_SETLOGIN 0 # endif # if defined(USE_PAM) && defined(HAVE_INITGROUPS) if (!restoreGids()) goto cError; # endif # ifndef HAVE_SETUSERCONTEXT # ifdef USE_PAM if (!setUid( curuser, curuid )) goto cError; # else if (!setUser( curuser, curuid, curgid )) goto cError; # endif # else /* !HAVE_SETUSERCONTEXT */ /* * Destroy environment. * We need to do this before setusercontext() because that may * set or reset some environment variables. */ if (!(environ = initStrArr( 0 ))) goto cError; /* * Set the user's credentials: uid, gid, groups, * environment variables, resource limits, and umask. */ if (setusercontext( lc, p, p->pw_uid, LOGIN_SETALL & ~(D_LOGIN_SETGROUP|D_LOGIN_SETLOGIN) ) < 0) { logError( "setusercontext for %s failed: %m\n", curuser ); goto cError; } for (i = 0; environ[i]; i++) userEnviron = putEnv( environ[i], userEnviron ); # endif /* !HAVE_SETUSERCONTEXT */ #else /* PAM || !_AIX */ /* * Set the user's credentials: uid, gid, groups, * audit classes, user limits, and umask. */ if (setpcred( curuser, NULL ) == -1) { logError( "setpcred for %s failed: %m\n", curuser ); goto cError; } /* * Set the users process environment. Store protected variables and * obtain updated user environment list. This call will initialize * global 'newenv'. */ if (setpenv( curuser, PENV_INIT | PENV_ARGV | PENV_NOEXEC, userEnviron, NULL ) != 0) { logError( "Cannot set %s's process environment\n", curuser ); goto cError; } userEnviron = newenv; #endif /* _AIX */ /* * for user-based authorization schemes, * use the password to get the user's credentials. */ #ifdef SECURE_RPC /* do like "keylogin" program */ if (!curpass[0]) logInfo( "No password for NIS provided.\n" ); else { char netname[MAXNETNAMELEN+1], secretkey[HEXKEYBYTES+1]; int nameret, keyret; int len; int key_set_ok = 0; struct key_netstarg netst; nameret = getnetname( netname ); debug( "user netname: %s\n", netname ); len = strlen( curpass ); if (len > 8) bzero( curpass + 8, len - 8 ); keyret = getsecretkey( netname, secretkey, curpass ); debug( "getsecretkey returns %d, key length %d\n", keyret, strlen( secretkey ) ); netst.st_netname = netname; memcpy( netst.st_priv_key, secretkey, HEXKEYBYTES ); memset( netst.st_pub_key, 0, HEXKEYBYTES ); if (key_setnet( &netst ) < 0) debug( "Could not set secret key.\n" ); /* is there a key, and do we have the right password? */ if (keyret == 1) { if (*secretkey) { keyret = key_setsecret( secretkey ); debug( "key_setsecret returns %d\n", keyret ); if (keyret == -1) logError( "Failed to set NIS secret key\n" ); else key_set_ok = 1; } else { /* found a key, but couldn't interpret it */ logError( "Password incorrect for NIS principal %s\n", nameret ? netname : curuser ); } } if (!key_set_ok) nukeAuth( 9, "SUN-DES-1" ); bzero( secretkey, strlen( secretkey ) ); } #endif #ifdef K5AUTH /* do like "kinit" program */ if (!curpass[0]) logInfo( "No password for Kerberos5 provided.\n" ); else if ((str = krb5Init( curuser, curpass, td->name ))) userEnviron = setEnv( userEnviron, "KRB5CCNAME", str ); else nukeAuth( 14, "MIT-KERBEROS-5" ); #endif /* K5AUTH */ if (td->autoReLogin) { gSet( &mstrtalk ); gSendInt( D_ReLogin ); gSendStr( curuser ); gSendStr( curpass ); gSendStr( newdmrc ); } if (curpass) bzero( curpass, strlen( curpass ) ); setUserAuthorization( td ); home = getEnv( userEnviron, "HOME" ); if (home && chdir( home ) < 0) { logError( "Cannot chdir to %s's home %s: %m\n", curuser, home ); sendStr( V_MSG_ERR, "Cannot enter home directory. Using /.\n" ); chdir( "/" ); userEnviron = setEnv( userEnviron, "HOME", "/" ); home = 0; } if (home || td->clientLogFile[0] == '/') { if (!createClientLog( td->clientLogFile )) { logWarn( "Session log file according to %s cannot be created: %m\n", td->clientLogFile ); goto tmperr; } } else { tmperr: if (!createClientLog( td->clientLogFallback )) logError( "Fallback session log file according to %s cannot be created: %m\n", td->clientLogFallback ); /* Could inform the user, but I guess this is only confusing. */ } if (!*dmrcDir) mergeSessionArgs( home != 0 ); if (!(desksess = iniEntry( curdmrc, "Desktop", "Session", 0 ))) desksess = "failsafe"; /* only due to OOM */ gSet( &mstrtalk ); gSendInt( D_User ); gSendInt( curuid ); gSendStr( curuser ); gSendStr( desksess ); close( mstrtalk.pipe->fd.w ); userEnviron = setEnv( userEnviron, "DESKTOP_SESSION", desksess ); for (i = 0; td->sessionsDirs[i]; i++) { fname = 0; if (strApp( &fname, td->sessionsDirs[i], "/", desksess, ".desktop", (char *)0 )) { if ((str = iniLoad( fname ))) { if (!strCmp( iniEntry( str, "Desktop Entry", "Hidden", 0 ), "true" ) || !(sessargs = iniEntry( str, "Desktop Entry", "Exec", 0 ))) sessargs = ""; free( str ); free( fname ); goto gotit; } free( fname ); } } if (!strcmp( desksess, "failsafe" ) || !strcmp( desksess, "default" ) || !strcmp( desksess, "custom" )) sessargs = desksess; else sessargs = ""; gotit: if (!(argv = parseArgs( (char **)0, td->session )) || !(argv = addStrArr( argv, sessargs, -1 ))) exit( 1 ); if (argv[0] && *argv[0]) { debug( "executing session %\"[s\n", argv ); execute( argv, userEnviron ); logError( "Session %\"s execution failed: %m\n", argv[0] ); } else logError( "Session has no command/arguments\n" ); failsafeArgv[0] = td->failsafeClient; failsafeArgv[1] = 0; execute( failsafeArgv, userEnviron ); logError( "Failsafe client %\"s execution failed: %m\n", failsafeArgv[0] ); cError: sendStr( V_MSG_ERR, 0 ); exit( 1 ); case -1: free( buf ); return 0; } debug( "StartSession, fork succeeded %d\n", *pid ); free( buf ); gSet( &ctltalk ); if (!Setjmp( ctltalk.errjmp )) while (gRecvCmd( &i )) { buf = gRecvStr(); displayStr( i, buf ); free( buf ); gSet( &ctltalk ); gSendInt( 0 ); } gClosen( ctltalk.pipe ); finishGreet(); return 1; }