void ch_user(void) { int jid, ngroups; uid_t huid; struct passwd *husername, *jusername; gid_t groups[NGROUPS]; login_cap_t *lcap; /* Get the current user ID and user name in the host system */ huid = getuid(); husername = getpwuid(huid); /* Get the user name in the jail */ jusername = getpwuid(huid); if (jusername == NULL || strcmp(husername->pw_name, jusername->pw_name) != 0) err(1, "Username mapping failed"); lcap = login_getpwclass(jusername); if (lcap == NULL) { err(1, "getpwclass: %s", jusername->pw_name); } ngroups = NGROUPS; if (getgrouplist(jusername->pw_name, jusername->pw_gid, groups, &ngroups) != 0) err(1, "getgrouplist: %s", jusername->pw_name); if (setgroups(ngroups, groups) != 0) err(1, "setgroups"); if (setgid(jusername->pw_gid) != 0) err(1, "setgid"); if (setusercontext(lcap, jusername, jusername->pw_uid, LOGIN_SETALL & ~LOGIN_SETGROUP & ~LOGIN_SETLOGIN) != 0) err(1, "setusercontext"); login_close(lcap); }
static void get_user_info(const char *username, const struct passwd **pwdp, login_cap_t **lcapp) { uid_t uid; const struct passwd *pwd; errno = 0; if (username) { pwd = getpwnam(username); if (pwd == NULL) { if (errno) err(1, "getpwnam: %s", username); else errx(1, "%s: no such user", username); } } else { uid = getuid(); pwd = getpwuid(uid); if (pwd == NULL) { if (errno) err(1, "getpwuid: %d", uid); else errx(1, "unknown uid: %d", uid); } } *pwdp = pwd; *lcapp = login_getpwclass(pwd); if (*lcapp == NULL) err(1, "getpwclass: %s", pwd->pw_name); if (initgroups(pwd->pw_name, pwd->pw_gid) < 0) err(1, "initgroups: %s", pwd->pw_name); }
static int pipe_transport_setup(transport_instance *tblock, address_item *addrlist, transport_feedback *dummy, uid_t uid, gid_t gid, uschar **errmsg) { pipe_transport_options_block *ob = (pipe_transport_options_block *)(tblock->options_block); addrlist = addrlist; /* Keep compiler happy */ dummy = dummy; uid = uid; gid = gid; errmsg = errmsg; ob = ob; #ifdef HAVE_SETCLASSRESOURCES if (ob->use_classresources) { struct passwd *pw = getpwuid(uid); if (pw != NULL) { login_cap_t *lc = login_getpwclass(pw); if (lc != NULL) { setclassresources(lc); login_close(lc); } } } #endif #ifdef RLIMIT_CORE if (ob->permit_coredump) { struct rlimit rl; rl.rlim_cur = RLIM_INFINITY; rl.rlim_max = RLIM_INFINITY; if (setrlimit(RLIMIT_CORE, &rl) < 0) { #ifdef SETRLIMIT_NOT_SUPPORTED if (errno != ENOSYS && errno != ENOTSUP) #endif log_write(0, LOG_MAIN, "delivery setrlimit(RLIMIT_CORE, RLIM_INFINITY) failed: %s", strerror(errno)); } } #endif return OK; }
/* * Set the environment to what would be expected if the user logged in * again; this performs the same steps as su(1)'s -l option. */ static void loginshell(void) { char *args[2], **cleanenv, *term, *ticket; const char *shell; login_cap_t *lc; shell = pwd->pw_shell; if (*shell == '\0') shell = _PATH_BSHELL; if (chdir(pwd->pw_dir) < 0) { warn("%s", pwd->pw_dir); chdir("/"); } term = getenv("TERM"); ticket = getenv("KRBTKFILE"); if ((cleanenv = calloc(20, sizeof(char *))) == NULL) err(1, "calloc"); *cleanenv = NULL; environ = cleanenv; lc = login_getpwclass(pwd); setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETPATH|LOGIN_SETUMASK|LOGIN_SETENV); login_close(lc); setenv("USER", pwd->pw_name, 1); setenv("SHELL", shell, 1); setenv("HOME", pwd->pw_dir, 1); if (term != NULL) setenv("TERM", term, 1); if (ticket != NULL) setenv("KRBTKFILE", ticket, 1); if (asprintf(args, "-%s", basename(shell)) < 0) err(1, "asprintf"); args[1] = NULL; execv(shell, args); err(1, "%s", shell); }
int pw_user(struct userconf * cnf, int mode, struct cargs * args) { int rc, edited = 0; char *p = NULL; char *passtmp; struct carg *a_name; struct carg *a_uid; struct carg *arg; struct passwd *pwd = NULL; struct group *grp; struct stat st; char line[_PASSWORD_LEN+1]; FILE *fp; char *dmode_c; void *set = NULL; static struct passwd fakeuser = { NULL, "*", -1, -1, 0, "", "User &", "/nonexistent", "/bin/sh", 0 #if defined(__FreeBSD__) ,0 #endif }; /* * With M_NEXT, we only need to return the * next uid to stdout */ if (mode == M_NEXT) { uid_t next = pw_uidpolicy(cnf, args); if (getarg(args, 'q')) return next; printf("%ld:", (long)next); pw_group(cnf, mode, args); return EXIT_SUCCESS; } /* * We can do all of the common legwork here */ if ((arg = getarg(args, 'b')) != NULL) { cnf->home = arg->val; } if ((arg = getarg(args, 'M')) != NULL) { dmode_c = arg->val; if ((set = setmode(dmode_c)) == NULL) errx(EX_DATAERR, "invalid directory creation mode '%s'", dmode_c); cnf->homemode = getmode(set, _DEF_DIRMODE); free(set); } /* * If we'll need to use it or we're updating it, * then create the base home directory if necessary */ if (arg != NULL || getarg(args, 'm') != NULL) { int l = strlen(cnf->home); if (l > 1 && cnf->home[l-1] == '/') /* Shave off any trailing path delimiter */ cnf->home[--l] = '\0'; if (l < 2 || *cnf->home != '/') /* Check for absolute path name */ errx(EX_DATAERR, "invalid base directory for home '%s'", cnf->home); if (stat(cnf->home, &st) == -1) { char dbuf[MAXPATHLEN]; /* * This is a kludge especially for Joerg :) * If the home directory would be created in the root partition, then * we really create it under /usr which is likely to have more space. * But we create a symlink from cnf->home -> "/usr" -> cnf->home */ if (strchr(cnf->home+1, '/') == NULL) { strcpy(dbuf, "/usr"); strncat(dbuf, cnf->home, MAXPATHLEN-5); if (mkdir(dbuf, _DEF_DIRMODE) != -1 || errno == EEXIST) { chown(dbuf, 0, 0); /* * Skip first "/" and create symlink: * /home -> usr/home */ symlink(dbuf+1, cnf->home); } /* If this falls, fall back to old method */ } strlcpy(dbuf, cnf->home, sizeof(dbuf)); p = dbuf; if (stat(dbuf, &st) == -1) { while ((p = strchr(p + 1, '/')) != NULL) { *p = '\0'; if (stat(dbuf, &st) == -1) { if (mkdir(dbuf, _DEF_DIRMODE) == -1) goto direrr; chown(dbuf, 0, 0); } else if (!S_ISDIR(st.st_mode)) errx(EX_OSFILE, "'%s' (root home parent) is not a directory", dbuf); *p = '/'; } } if (stat(dbuf, &st) == -1) { if (mkdir(dbuf, _DEF_DIRMODE) == -1) { direrr: err(EX_OSFILE, "mkdir '%s'", dbuf); } chown(dbuf, 0, 0); } } else if (!S_ISDIR(st.st_mode)) errx(EX_OSFILE, "root home `%s' is not a directory", cnf->home); } if ((arg = getarg(args, 'e')) != NULL) cnf->expire_days = atoi(arg->val); if ((arg = getarg(args, 'y')) != NULL) cnf->nispasswd = arg->val; if ((arg = getarg(args, 'p')) != NULL && arg->val) cnf->password_days = atoi(arg->val); if ((arg = getarg(args, 'g')) != NULL) { if (!*(p = arg->val)) /* Handle empty group list specially */ cnf->default_group = ""; else { if ((grp = GETGRNAM(p)) == NULL) { if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL) errx(EX_NOUSER, "group `%s' does not exist", p); } cnf->default_group = newstr(grp->gr_name); } } if ((arg = getarg(args, 'L')) != NULL) cnf->default_class = pw_checkname((u_char *)arg->val, 0); if ((arg = getarg(args, 'G')) != NULL && arg->val) { int i = 0; for (p = strtok(arg->val, ", \t"); p != NULL; p = strtok(NULL, ", \t")) { if ((grp = GETGRNAM(p)) == NULL) { if (!isdigit((unsigned char)*p) || (grp = GETGRGID((gid_t) atoi(p))) == NULL) errx(EX_NOUSER, "group `%s' does not exist", p); } if (extendarray(&cnf->groups, &cnf->numgroups, i + 2) != -1) cnf->groups[i++] = newstr(grp->gr_name); } while (i < cnf->numgroups) cnf->groups[i++] = NULL; } if ((arg = getarg(args, 'k')) != NULL) { if (stat(cnf->dotdir = arg->val, &st) == -1 || !S_ISDIR(st.st_mode)) errx(EX_OSFILE, "skeleton `%s' is not a directory or does not exist", cnf->dotdir); } if ((arg = getarg(args, 's')) != NULL) cnf->shell_default = arg->val; if ((arg = getarg(args, 'w')) != NULL) cnf->default_password = boolean_val(arg->val, cnf->default_password); if (mode == M_ADD && getarg(args, 'D')) { if (getarg(args, 'n') != NULL) errx(EX_DATAERR, "can't combine `-D' with `-n name'"); if ((arg = getarg(args, 'u')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) { if ((cnf->min_uid = (uid_t) atoi(p)) == 0) cnf->min_uid = 1000; if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_uid = (uid_t) atoi(p)) < cnf->min_uid) cnf->max_uid = 32000; } if ((arg = getarg(args, 'i')) != NULL && (p = strtok(arg->val, ", \t")) != NULL) { if ((cnf->min_gid = (gid_t) atoi(p)) == 0) cnf->min_gid = 1000; if ((p = strtok(NULL, " ,\t")) == NULL || (cnf->max_gid = (gid_t) atoi(p)) < cnf->min_gid) cnf->max_gid = 32000; } arg = getarg(args, 'C'); if (write_userconfig(arg ? arg->val : NULL)) return EXIT_SUCCESS; warn("config update"); return EX_IOERR; } if (mode == M_PRINT && getarg(args, 'a')) { int pretty = getarg(args, 'P') != NULL; int v7 = getarg(args, '7') != NULL; SETPWENT(); while ((pwd = GETPWENT()) != NULL) print_user(pwd, pretty, v7); ENDPWENT(); return EXIT_SUCCESS; } if ((a_name = getarg(args, 'n')) != NULL) pwd = GETPWNAM(pw_checkname((u_char *)a_name->val, 0)); a_uid = getarg(args, 'u'); if (a_uid == NULL) { if (a_name == NULL) errx(EX_DATAERR, "user name or id required"); /* * Determine whether 'n' switch is name or uid - we don't * really don't really care which we have, but we need to * know. */ if (mode != M_ADD && pwd == NULL && strspn(a_name->val, "0123456789") == strlen(a_name->val) && *a_name->val) { (a_uid = a_name)->ch = 'u'; a_name = NULL; } } /* * Update, delete & print require that the user exists */ if (mode == M_UPDATE || mode == M_DELETE || mode == M_PRINT || mode == M_LOCK || mode == M_UNLOCK) { if (a_name == NULL && pwd == NULL) /* Try harder */ pwd = GETPWUID(atoi(a_uid->val)); if (pwd == NULL) { if (mode == M_PRINT && getarg(args, 'F')) { fakeuser.pw_name = a_name ? a_name->val : "nouser"; fakeuser.pw_uid = a_uid ? (uid_t) atol(a_uid->val) : -1; return print_user(&fakeuser, getarg(args, 'P') != NULL, getarg(args, '7') != NULL); } if (a_name == NULL) errx(EX_NOUSER, "no such uid `%s'", a_uid->val); errx(EX_NOUSER, "no such user `%s'", a_name->val); } if (a_name == NULL) /* May be needed later */ a_name = addarg(args, 'n', newstr(pwd->pw_name)); /* * The M_LOCK and M_UNLOCK functions simply add or remove * a "*LOCKED*" prefix from in front of the password to * prevent it decoding correctly, and therefore prevents * access. Of course, this only prevents access via * password authentication (not ssh, kerberos or any * other method that does not use the UNIX password) but * that is a known limitation. */ if (mode == M_LOCK) { if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) == 0) errx(EX_DATAERR, "user '%s' is already locked", pwd->pw_name); passtmp = malloc(strlen(pwd->pw_passwd) + sizeof(locked_str)); if (passtmp == NULL) /* disaster */ errx(EX_UNAVAILABLE, "out of memory"); strcpy(passtmp, locked_str); strcat(passtmp, pwd->pw_passwd); pwd->pw_passwd = passtmp; edited = 1; } else if (mode == M_UNLOCK) { if (strncmp(pwd->pw_passwd, locked_str, sizeof(locked_str)-1) != 0) errx(EX_DATAERR, "user '%s' is not locked", pwd->pw_name); pwd->pw_passwd += sizeof(locked_str)-1; edited = 1; } else if (mode == M_DELETE) { /* * Handle deletions now */ char file[MAXPATHLEN]; char home[MAXPATHLEN]; uid_t uid = pwd->pw_uid; struct group *gr; char grname[LOGNAMESIZE]; if (strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "cannot remove user 'root'"); if (!PWALTDIR()) { /* * Remove opie record from /etc/opiekeys */ rmopie(pwd->pw_name); /* * Remove crontabs */ snprintf(file, sizeof(file), "/var/cron/tabs/%s", pwd->pw_name); if (access(file, F_OK) == 0) { sprintf(file, "crontab -u %s -r", pwd->pw_name); system(file); } } /* * Save these for later, since contents of pwd may be * invalidated by deletion */ sprintf(file, "%s/%s", _PATH_MAILDIR, pwd->pw_name); strlcpy(home, pwd->pw_dir, sizeof(home)); gr = GETGRGID(pwd->pw_gid); if (gr != NULL) strlcpy(grname, gr->gr_name, LOGNAMESIZE); else grname[0] = '\0'; rc = delpwent(pwd); if (rc == -1) err(EX_IOERR, "user '%s' does not exist", pwd->pw_name); else if (rc != 0) { warn("passwd update"); return EX_IOERR; } if (cnf->nispasswd && *cnf->nispasswd=='/') { rc = delnispwent(cnf->nispasswd, a_name->val); if (rc == -1) warnx("WARNING: user '%s' does not exist in NIS passwd", pwd->pw_name); else if (rc != 0) warn("WARNING: NIS passwd update"); /* non-fatal */ } grp = GETGRNAM(a_name->val); if (grp != NULL && (grp->gr_mem == NULL || *grp->gr_mem == NULL) && strcmp(a_name->val, grname) == 0) delgrent(GETGRNAM(a_name->val)); SETGRENT(); while ((grp = GETGRENT()) != NULL) { int i, j; char group[MAXLOGNAME]; if (grp->gr_mem != NULL) { for (i = 0; grp->gr_mem[i] != NULL; i++) { if (!strcmp(grp->gr_mem[i], a_name->val)) { for (j = i; grp->gr_mem[j] != NULL; j++) grp->gr_mem[j] = grp->gr_mem[j+1]; strlcpy(group, grp->gr_name, MAXLOGNAME); chggrent(group, grp); } } } } ENDGRENT(); pw_log(cnf, mode, W_USER, "%s(%ld) account removed", a_name->val, (long) uid); if (!PWALTDIR()) { /* * Remove mail file */ remove(file); /* * Remove at jobs */ if (getpwuid(uid) == NULL) rmat(uid); /* * Remove home directory and contents */ if (getarg(args, 'r') != NULL && *home == '/' && getpwuid(uid) == NULL) { if (stat(home, &st) != -1) { rm_r(home, uid); pw_log(cnf, mode, W_USER, "%s(%ld) home '%s' %sremoved", a_name->val, (long) uid, home, stat(home, &st) == -1 ? "" : "not completely "); } } } return EXIT_SUCCESS; } else if (mode == M_PRINT) return print_user(pwd, getarg(args, 'P') != NULL, getarg(args, '7') != NULL); /* * The rest is edit code */ if ((arg = getarg(args, 'l')) != NULL) { if (strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't rename `root' account"); pwd->pw_name = pw_checkname((u_char *)arg->val, 0); edited = 1; } if ((arg = getarg(args, 'u')) != NULL && isdigit((unsigned char)*arg->val)) { pwd->pw_uid = (uid_t) atol(arg->val); edited = 1; if (pwd->pw_uid != 0 && strcmp(pwd->pw_name, "root") == 0) errx(EX_DATAERR, "can't change uid of `root' account"); if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) warnx("WARNING: account `%s' will have a uid of 0 (superuser access!)", pwd->pw_name); } if ((arg = getarg(args, 'g')) != NULL && pwd->pw_uid != 0) { /* Already checked this */ gid_t newgid = (gid_t) GETGRNAM(cnf->default_group)->gr_gid; if (newgid != pwd->pw_gid) { edited = 1; pwd->pw_gid = newgid; } } if ((arg = getarg(args, 'p')) != NULL) { if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) { if (pwd->pw_change != 0) { pwd->pw_change = 0; edited = 1; } } else { time_t now = time(NULL); time_t expire = parse_date(now, arg->val); if (pwd->pw_change != expire) { pwd->pw_change = expire; edited = 1; } } } if ((arg = getarg(args, 'e')) != NULL) { if (*arg->val == '\0' || strcmp(arg->val, "0") == 0) { if (pwd->pw_expire != 0) { pwd->pw_expire = 0; edited = 1; } } else { time_t now = time(NULL); time_t expire = parse_date(now, arg->val); if (pwd->pw_expire != expire) { pwd->pw_expire = expire; edited = 1; } } } if ((arg = getarg(args, 's')) != NULL) { char *shell = shell_path(cnf->shelldir, cnf->shells, arg->val); if (shell == NULL) shell = ""; if (strcmp(shell, pwd->pw_shell) != 0) { pwd->pw_shell = shell; edited = 1; } } if (getarg(args, 'L')) { if (cnf->default_class == NULL) cnf->default_class = ""; if (strcmp(pwd->pw_class, cnf->default_class) != 0) { pwd->pw_class = cnf->default_class; edited = 1; } } if ((arg = getarg(args, 'd')) != NULL) { if (strcmp(pwd->pw_dir, arg->val)) edited = 1; if (stat(pwd->pw_dir = arg->val, &st) == -1) { if (getarg(args, 'm') == NULL && strcmp(pwd->pw_dir, "/nonexistent") != 0) warnx("WARNING: home `%s' does not exist", pwd->pw_dir); } else if (!S_ISDIR(st.st_mode)) warnx("WARNING: home `%s' is not a directory", pwd->pw_dir); } if ((arg = getarg(args, 'w')) != NULL && getarg(args, 'h') == NULL && getarg(args, 'H') == NULL) { login_cap_t *lc; lc = login_getpwclass(pwd); if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name); edited = 1; } } else { login_cap_t *lc; /* * Add code */ if (a_name == NULL) /* Required */ errx(EX_DATAERR, "login name required"); else if ((pwd = GETPWNAM(a_name->val)) != NULL) /* Exists */ errx(EX_DATAERR, "login name `%s' already exists", a_name->val); /* * Now, set up defaults for a new user */ pwd = &fakeuser; pwd->pw_name = a_name->val; pwd->pw_class = cnf->default_class ? cnf->default_class : ""; pwd->pw_uid = pw_uidpolicy(cnf, args); pwd->pw_gid = pw_gidpolicy(cnf, args, pwd->pw_name, (gid_t) pwd->pw_uid); pwd->pw_change = pw_pwdpolicy(cnf, args); pwd->pw_expire = pw_exppolicy(cnf, args); pwd->pw_dir = pw_homepolicy(cnf, args, pwd->pw_name); pwd->pw_shell = pw_shellpolicy(cnf, args, NULL); lc = login_getpwclass(pwd); if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); pwd->pw_passwd = pw_password(cnf, args, pwd->pw_name); edited = 1; if (pwd->pw_uid == 0 && strcmp(pwd->pw_name, "root") != 0) warnx("WARNING: new account `%s' has a uid of 0 (superuser access!)", pwd->pw_name); } /* * Shared add/edit code */ if ((arg = getarg(args, 'c')) != NULL) { char *gecos = pw_checkname((u_char *)arg->val, 1); if (strcmp(pwd->pw_gecos, gecos) != 0) { pwd->pw_gecos = gecos; edited = 1; } } if ((arg = getarg(args, 'h')) != NULL || (arg = getarg(args, 'H')) != NULL) { if (strcmp(arg->val, "-") == 0) { if (!pwd->pw_passwd || *pwd->pw_passwd != '*') { pwd->pw_passwd = "*"; /* No access */ edited = 1; } } else { int fd = atoi(arg->val); int precrypt = (arg->ch == 'H'); int b; int istty = isatty(fd); struct termios t; login_cap_t *lc; if (istty) { if (tcgetattr(fd, &t) == -1) istty = 0; else { struct termios n = t; /* Disable echo */ n.c_lflag &= ~(ECHO); tcsetattr(fd, TCSANOW, &n); printf("%s%spassword for user %s:", (mode == M_UPDATE) ? "new " : "", precrypt ? "encrypted " : "", pwd->pw_name); fflush(stdout); } } b = read(fd, line, sizeof(line) - 1); if (istty) { /* Restore state */ tcsetattr(fd, TCSANOW, &t); fputc('\n', stdout); fflush(stdout); } if (b < 0) { warn("-%c file descriptor", precrypt ? 'H' : 'h'); return EX_IOERR; } line[b] = '\0'; if ((p = strpbrk(line, "\r\n")) != NULL) *p = '\0'; if (!*line) errx(EX_DATAERR, "empty password read on file descriptor %d", fd); if (precrypt) { if (strchr(line, ':') != NULL) return EX_DATAERR; pwd->pw_passwd = line; } else { lc = login_getpwclass(pwd); if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); pwd->pw_passwd = pw_pwcrypt(line); } edited = 1; } } /* * Special case: -N only displays & exits */ if (getarg(args, 'N') != NULL) return print_user(pwd, getarg(args, 'P') != NULL, getarg(args, '7') != NULL); if (mode == M_ADD) { edited = 1; /* Always */ rc = addpwent(pwd); if (rc == -1) { warnx("user '%s' already exists", pwd->pw_name); return EX_IOERR; } else if (rc != 0) { warn("passwd file update"); return EX_IOERR; } if (cnf->nispasswd && *cnf->nispasswd=='/') { rc = addnispwent(cnf->nispasswd, pwd); if (rc == -1) warnx("User '%s' already exists in NIS passwd", pwd->pw_name); else warn("NIS passwd update"); /* NOTE: we treat NIS-only update errors as non-fatal */ } } else if (mode == M_UPDATE || mode == M_LOCK || mode == M_UNLOCK) { if (edited) { /* Only updated this if required */ rc = chgpwent(a_name->val, pwd); if (rc == -1) { warnx("user '%s' does not exist (NIS?)", pwd->pw_name); return EX_IOERR; } else if (rc != 0) { warn("passwd file update"); return EX_IOERR; } if ( cnf->nispasswd && *cnf->nispasswd=='/') { rc = chgnispwent(cnf->nispasswd, a_name->val, pwd); if (rc == -1) warn("User '%s' not found in NIS passwd", pwd->pw_name); else warn("NIS passwd update"); /* NOTE: NIS-only update errors are not fatal */ } } } /* * Ok, user is created or changed - now edit group file */ if (mode == M_ADD || getarg(args, 'G') != NULL) { int i; for (i = 0; cnf->groups[i] != NULL; i++) { grp = GETGRNAM(cnf->groups[i]); grp = gr_add(grp, pwd->pw_name); /* * grp can only be NULL in 2 cases: * - the new member is already a member * - a problem with memory occurs * in both cases we want to skip now. */ if (grp == NULL) continue; chggrent(cnf->groups[i], grp); free(grp); } } /* go get a current version of pwd */ pwd = GETPWNAM(a_name->val); if (pwd == NULL) { /* This will fail when we rename, so special case that */ if (mode == M_UPDATE && (arg = getarg(args, 'l')) != NULL) { a_name->val = arg->val; /* update new name */ pwd = GETPWNAM(a_name->val); /* refetch renamed rec */ } } if (pwd == NULL) /* can't go on without this */ errx(EX_NOUSER, "user '%s' disappeared during update", a_name->val); grp = GETGRGID(pwd->pw_gid); pw_log(cnf, mode, W_USER, "%s(%ld):%s(%ld):%s:%s:%s", pwd->pw_name, (long) pwd->pw_uid, grp ? grp->gr_name : "unknown", (long) (grp ? grp->gr_gid : -1), pwd->pw_gecos, pwd->pw_dir, pwd->pw_shell); /* * If adding, let's touch and chown the user's mail file. This is not * strictly necessary under BSD with a 0755 maildir but it also * doesn't hurt anything to create the empty mailfile */ if (mode == M_ADD) { if (!PWALTDIR()) { sprintf(line, "%s/%s", _PATH_MAILDIR, pwd->pw_name); close(open(line, O_RDWR | O_CREAT, 0600)); /* Preserve contents & * mtime */ chown(line, pwd->pw_uid, pwd->pw_gid); } } /* * Let's create and populate the user's home directory. Note * that this also `works' for editing users if -m is used, but * existing files will *not* be overwritten. */ if (!PWALTDIR() && getarg(args, 'm') != NULL && pwd->pw_dir && *pwd->pw_dir == '/' && pwd->pw_dir[1]) { copymkdir(pwd->pw_dir, cnf->dotdir, cnf->homemode, pwd->pw_uid, pwd->pw_gid); pw_log(cnf, mode, W_USER, "%s(%ld) home %s made", pwd->pw_name, (long) pwd->pw_uid, pwd->pw_dir); } /* * Finally, send mail to the new user as well, if we are asked to */ if (mode == M_ADD && !PWALTDIR() && cnf->newmail && *cnf->newmail && (fp = fopen(cnf->newmail, "r")) != NULL) { FILE *pfp = popen(_PATH_SENDMAIL " -t", "w"); if (pfp == NULL) warn("sendmail"); else { fprintf(pfp, "From: root\n" "To: %s\n" "Subject: Welcome!\n\n", pwd->pw_name); while (fgets(line, sizeof(line), fp) != NULL) { /* Do substitutions? */ fputs(line, pfp); } pclose(pfp); pw_log(cnf, mode, W_USER, "%s(%ld) new user mail sent", pwd->pw_name, (long) pwd->pw_uid); } fclose(fp); } return EXIT_SUCCESS; }
/*ARGSUSED*/ static void input_userauth_request(int type, u_int32_t seq, void *ctxt) { Authctxt *authctxt = ctxt; Authmethod *m = NULL; char *user, *service, *method, *style = NULL; int authenticated = 0; #ifdef HAVE_LOGIN_CAP login_cap_t *lc; const char *from_host, *from_ip; from_host = get_canonical_hostname(options.use_dns); from_ip = get_remote_ipaddr(); #endif if (authctxt == NULL) fatal("input_userauth_request: no authctxt"); user = packet_get_cstring(NULL); service = packet_get_cstring(NULL); method = packet_get_cstring(NULL); debug("userauth-request for user %s service %s method %s", user, service, method); debug("attempt %d failures %d", authctxt->attempt, authctxt->failures); if ((style = strchr(user, ':')) != NULL) *style++ = 0; if (authctxt->attempt++ == 0) { /* setup auth context */ authctxt->pw = PRIVSEP(getpwnamallow(user)); authctxt->user = xstrdup(user); if (authctxt->pw && strcmp(service, "ssh-connection")==0) { authctxt->valid = 1; debug2("input_userauth_request: setting up authctxt for %s", user); } else { logit("input_userauth_request: invalid user %s", user); authctxt->pw = fakepw(); #ifdef SSH_AUDIT_EVENTS PRIVSEP(audit_event(SSH_INVALID_USER)); #endif } #ifdef USE_PAM if (options.use_pam) PRIVSEP(start_pam(authctxt)); #endif setproctitle("%s%s", authctxt->valid ? user : "******", use_privsep ? " [net]" : ""); authctxt->service = xstrdup(service); authctxt->style = style ? xstrdup(style) : NULL; if (use_privsep) mm_inform_authserv(service, style); userauth_banner(); if (auth2_setup_methods_lists(authctxt) != 0) packet_disconnect("no authentication methods enabled"); } else if (strcmp(user, authctxt->user) != 0 || strcmp(service, authctxt->service) != 0) { packet_disconnect("Change of username or service not allowed: " "(%s,%s) -> (%s,%s)", authctxt->user, authctxt->service, user, service); } #ifdef HAVE_LOGIN_CAP if (authctxt->pw != NULL) { lc = login_getpwclass(authctxt->pw); if (lc == NULL) lc = login_getclassbyname(NULL, authctxt->pw); if (!auth_hostok(lc, from_host, from_ip)) { logit("Denied connection for %.200s from %.200s [%.200s].", authctxt->pw->pw_name, from_host, from_ip); packet_disconnect("Sorry, you are not allowed to connect."); } if (!auth_timeok(lc, time(NULL))) { logit("LOGIN %.200s REFUSED (TIME) FROM %.200s", authctxt->pw->pw_name, from_host); packet_disconnect("Logins not available right now."); } login_close(lc); lc = NULL; } #endif /* HAVE_LOGIN_CAP */ /* reset state */ auth2_challenge_stop(authctxt); #ifdef GSSAPI /* XXX move to auth2_gssapi_stop() */ dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_TOKEN, NULL); dispatch_set(SSH2_MSG_USERAUTH_GSSAPI_EXCHANGE_COMPLETE, NULL); #endif authctxt->postponed = 0; authctxt->server_caused_failure = 0; /* try to authenticate user */ m = authmethod_lookup(authctxt, method); if (m != NULL && authctxt->failures < options.max_authtries) { debug2("input_userauth_request: try method %s", method); authenticated = m->userauth(authctxt); } userauth_finish(authctxt, authenticated, method, NULL); free(service); free(user); free(method); }
int setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags) { quad_t p; mode_t mymask; login_cap_t *llc = NULL; struct sigaction sa, prevsa; struct rtprio rtp; int error; if (lc == NULL) { if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL) llc = lc; /* free this when we're done */ } if (flags & LOGIN_SETPATH) pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH; /* we need a passwd entry to set these */ if (pwd == NULL) flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN | LOGIN_SETMAC); /* Set the process priority */ if (flags & LOGIN_SETPRIORITY) { p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI); if (p > PRIO_MAX) { rtp.type = RTP_PRIO_IDLE; rtp.prio = p - PRIO_MAX - 1; p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p; if (rtprio(RTP_SET, 0, &rtp)) syslog(LOG_WARNING, "rtprio '%s' (%s): %m", pwd ? pwd->pw_name : "-", lc ? lc->lc_class : LOGIN_DEFCLASS); } else if (p < PRIO_MIN) { rtp.type = RTP_PRIO_REALTIME; rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX); p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p; if (rtprio(RTP_SET, 0, &rtp)) syslog(LOG_WARNING, "rtprio '%s' (%s): %m", pwd ? pwd->pw_name : "-", lc ? lc->lc_class : LOGIN_DEFCLASS); } else { if (setpriority(PRIO_PROCESS, 0, (int)p) != 0) syslog(LOG_WARNING, "setpriority '%s' (%s): %m", pwd ? pwd->pw_name : "-", lc ? lc->lc_class : LOGIN_DEFCLASS); } } /* Setup the user's group permissions */ if (flags & LOGIN_SETGROUP) { if (setgid(pwd->pw_gid) != 0) { syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid); login_close(llc); return (-1); } if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name, (u_long)pwd->pw_gid); login_close(llc); return (-1); } } /* Set up the user's MAC label. */ if ((flags & LOGIN_SETMAC) && mac_is_present(NULL) == 1) { const char *label_string; mac_t label; label_string = login_getcapstr(lc, "label", NULL, NULL); if (label_string != NULL) { if (mac_from_text(&label, label_string) == -1) { syslog(LOG_ERR, "mac_from_text('%s') for %s: %m", pwd->pw_name, label_string); return (-1); } if (mac_set_proc(label) == -1) error = errno; else error = 0; mac_free(label); if (error != 0) { syslog(LOG_ERR, "mac_set_proc('%s') for %s: %s", label_string, pwd->pw_name, strerror(error)); return (-1); } } } /* Set the sessions login */ if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) { syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name); login_close(llc); return (-1); } /* Inform the kernel about current login class */ if (lc != NULL && lc->lc_class != NULL && (flags & LOGIN_SETLOGINCLASS)) { /* * XXX: This is a workaround to fail gracefully in case the kernel * does not support setloginclass(2). */ bzero(&sa, sizeof(sa)); sa.sa_handler = SIG_IGN; sigfillset(&sa.sa_mask); sigaction(SIGSYS, &sa, &prevsa); error = setloginclass(lc->lc_class); sigaction(SIGSYS, &prevsa, NULL); if (error != 0) { syslog(LOG_ERR, "setloginclass(%s): %m", lc->lc_class); #ifdef notyet login_close(llc); return (-1); #endif } } mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0; mymask = setlogincontext(lc, pwd, mymask, flags); login_close(llc); /* This needs to be done after anything that needs root privs */ if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) { syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid); return (-1); /* Paranoia again */ } /* * Now, we repeat some of the above for the user's private entries */ if (getuid() == uid && (lc = login_getuserclass(pwd)) != NULL) { mymask = setlogincontext(lc, pwd, mymask, flags); login_close(lc); } /* Finally, set any umask we've found */ if (flags & LOGIN_SETUMASK) umask(mymask); return (0); }
void doit(struct sockaddr *fromp) { extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ struct passwd *pwd; u_short port; fd_set ready, readfrom; int cc, fd, nfd, pv[2], pid, s; int one = 1; const char *cp, *errorstr; char sig, buf[BUFSIZ]; char *cmdbuf, luser[16], ruser[16]; char rhost[2 * MAXHOSTNAMELEN + 1]; char numericname[INET6_ADDRSTRLEN]; int af, srcport; int maxcmdlen; #ifndef __APPLE__ login_cap_t *lc; #else struct hostent *hp; char *hostname, *errorhost = NULL; #endif maxcmdlen = (int)sysconf(_SC_ARG_MAX); if (maxcmdlen <= 0 || (cmdbuf = malloc(maxcmdlen)) == NULL) exit(1); #if defined(KERBEROS) AUTH_DAT *kdata = (AUTH_DAT *) NULL; KTEXT ticket = (KTEXT) NULL; char instance[INST_SZ], version[VERSION_SIZE]; struct sockaddr_in fromaddr; int rc; long authopts; int pv1[2], pv2[2]; fd_set wready, writeto; fromaddr = *fromp; #endif /* KERBEROS */ (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); (void) signal(SIGTERM, SIG_DFL); af = fromp->sa_family; srcport = ntohs(*((in_port_t *)&fromp->sa_data)); if (af == AF_INET) { inet_ntop(af, &((struct sockaddr_in *)fromp)->sin_addr, numericname, sizeof numericname); } else if (af == AF_INET6) { inet_ntop(af, &((struct sockaddr_in6 *)fromp)->sin6_addr, numericname, sizeof numericname); } else { syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); exit(1); } #ifdef IP_OPTIONS if (af == AF_INET) { u_char optbuf[BUFSIZ/3]; socklen_t optsize = sizeof(optbuf), ipproto, i; struct protoent *ip; if ((ip = getprotobyname("ip")) != NULL) ipproto = ip->p_proto; else ipproto = IPPROTO_IP; if (!getsockopt(0, ipproto, IP_OPTIONS, optbuf, &optsize) && optsize != 0) { for (i = 0; i < optsize; ) { u_char c = optbuf[i]; if (c == IPOPT_LSRR || c == IPOPT_SSRR) { syslog(LOG_NOTICE, "connection refused from %s with IP option %s", numericname, c == IPOPT_LSRR ? "LSRR" : "SSRR"); exit(1); } if (c == IPOPT_EOL) break; i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; } } } #endif #if defined(KERBEROS) if (!use_kerberos) #endif if (srcport >= IPPORT_RESERVED || srcport < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "connection from %s on illegal port %u", numericname, srcport); exit(1); } (void) alarm(60); port = 0; s = 0; /* not set or used if port == 0 */ for (;;) { char c; if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { if (cc < 0) syslog(LOG_NOTICE, "read: %m"); shutdown(0, SHUT_RDWR); exit(1); } if (c == 0) break; port = port * 10 + c - '0'; } (void) alarm(0); if (port != 0) { int lport = IPPORT_RESERVED - 1; s = rresvport_af(&lport, af); if (s < 0) { syslog(LOG_ERR, "can't get stderr port: %m"); exit(1); } #if defined(KERBEROS) if (!use_kerberos) #endif if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "2nd socket from %s on unreserved port %u", numericname, port); exit(1); } *((in_port_t *)&fromp->sa_data) = htons(port); if (connect(s, fromp, fromp->sa_len) < 0) { syslog(LOG_INFO, "connect second port %d: %m", port); exit(1); } } #if defined(KERBEROS) if (vacuous) { error("rshd: remote host requires Kerberos authentication\n"); exit(1); } #endif errorstr = NULL; #ifndef __APPLE__ realhostname_sa(rhost, sizeof(rhost) - 1, fromp, fromp->sa_len); rhost[sizeof(rhost) - 1] = '\0'; /* XXX truncation! */ #else errorstr = NULL; hp = gethostbyaddr((char *)&((struct sockaddr_in *)fromp)->sin_addr, sizeof (struct in_addr), ((struct sockaddr_in *)fromp)->sin_family); if (hp) { /* * If name returned by gethostbyaddr is in our domain, * attempt to verify that we haven't been fooled by someone * in a remote net; look up the name and check that this * address corresponds to the name. */ hostname = hp->h_name; #if defined(KERBEROS) if (!use_kerberos) #endif if (check_all || local_domain(hp->h_name)) { strncpy(rhost, hp->h_name, sizeof(rhost) - 1); rhost[sizeof(rhost) - 1] = 0; errorhost = rhost; hp = gethostbyname(rhost); if (hp == NULL) { syslog(LOG_INFO, "Couldn't look up address for %s", rhost); errorstr = "Couldn't look up address for your host (%s)\n"; hostname = inet_ntoa(((struct sockaddr_in *)fromp)->sin_addr); } else for (; ; hp->h_addr_list++) { if (hp->h_addr_list[0] == NULL) { syslog(LOG_NOTICE, "Host addr %s not listed for host %s", inet_ntoa(((struct sockaddr_in *)fromp)->sin_addr), hp->h_name); errorstr = "Host address mismatch for %s\n"; hostname = inet_ntoa(((struct sockaddr_in *)fromp)->sin_addr); break; } if (!bcmp(hp->h_addr_list[0], (caddr_t)&((struct sockaddr_in *)fromp)->sin_addr, sizeof(((struct sockaddr_in *)fromp)->sin_addr))) { hostname = hp->h_name; break; } } } } else errorhost = hostname = inet_ntoa(((struct sockaddr_in *)fromp)->sin_addr); #if defined(KERBEROS) if (use_kerberos) { kdata = (AUTH_DAT *) authbuf; ticket = (KTEXT) tickbuf; authopts = 0L; strcpy(instance, "*"); version[VERSION_SIZE - 1] = '\0'; #if defined(CRYPT) if (doencrypt) { struct sockaddr_in local_addr; rc = sizeof(local_addr); if (getsockname(0, (struct sockaddr *)&local_addr, &rc) < 0) { syslog(LOG_ERR, "getsockname: %m"); error("rshd: getsockname: %m"); exit(1); } authopts = KOPT_DO_MUTUAL; rc = krb_recvauth(authopts, 0, ticket, "rcmd", instance, &fromaddr, &local_addr, kdata, "", schedule, version); des_set_key(kdata->session, schedule); } else #endif /* CRYPT */ rc = krb_recvauth(authopts, 0, ticket, "rcmd", instance, &fromaddr, (struct sockaddr_in *) 0, kdata, "", (bit_64 *) 0, version); if (rc != KSUCCESS) { error("Kerberos authentication failure: %s\n", krb_err_txt[rc]); exit(1); } } else #endif /* KERBEROS */ #endif (void) alarm(60); getstr(ruser, sizeof(ruser), "ruser"); getstr(luser, sizeof(luser), "luser"); getstr(cmdbuf, maxcmdlen, "command"); (void) alarm(0); #if !TARGET_OS_EMBEDDED pam_err = pam_start("rshd", luser, &pamc, &pamh); if (pam_err != PAM_SUCCESS) { syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s", pam_strerror(pamh, pam_err)); rshd_errx(1, "Login incorrect."); } if ((pam_err = pam_set_item(pamh, PAM_RUSER, ruser)) != PAM_SUCCESS || (pam_err = pam_set_item(pamh, PAM_RHOST, rhost) != PAM_SUCCESS)) { syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s", pam_strerror(pamh, pam_err)); rshd_errx(1, "Login incorrect."); } pam_err = pam_authenticate(pamh, 0); if (pam_err == PAM_SUCCESS) { if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) { strncpy(luser, cp, sizeof(luser)); luser[sizeof(luser) - 1] = '\0'; /* XXX truncation! */ } pam_err = pam_acct_mgmt(pamh, 0); } if (pam_err != PAM_SUCCESS) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", ruser, rhost, luser, pam_strerror(pamh, pam_err), cmdbuf); rshd_errx(1, "Login incorrect."); } #endif setpwent(); pwd = getpwnam(luser); if (pwd == NULL) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: unknown login. cmd='%.80s'", ruser, rhost, luser, cmdbuf); if (errorstr == NULL) errorstr = "Login incorrect."; rshd_errx(1, errorstr, rhost); } #ifndef __APPLE__ lc = login_getpwclass(pwd); if (pwd->pw_uid) auth_checknologin(lc); #endif if (chdir(pwd->pw_dir) < 0) { if (chdir("/") < 0 || #ifndef __APPLE__ login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) { #else 0) { #endif /* __APPLE__ */ #ifdef notdef syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: no home directory. cmd='%.80s'", ruser, rhost, luser, cmdbuf); rshd_errx(0, "No remote home directory."); #endif } pwd->pw_dir = slash; } #if defined(KERBEROS) if (use_kerberos) { if (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0') { if (kuserok(kdata, luser) != 0) { syslog(LOG_INFO|LOG_AUTH, "Kerberos rsh denied to %s.%s@%s", kdata->pname, kdata->pinst, kdata->prealm); error("Permission denied.\n"); exit(1); } } } else #endif #ifdef __APPLE__ if (errorstr || (pwd->pw_passwd != 0 && *pwd->pw_passwd != '\0' && iruserok(((struct sockaddr_in *)fromp)->sin_addr.s_addr, #if TARGET_OS_EMBEDDED // rdar://problem/5381734 0, #else pwd->pw_uid == 0, #endif ruser, luser) < 0)) { if (__rcmd_errstr) syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", ruser, rhost, luser, __rcmd_errstr, cmdbuf); else syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied. cmd='%.80s'", ruser, rhost, luser, cmdbuf); if (errorstr == NULL) errorstr = "Permission denied."; rshd_errx(1, errorstr, errorhost); } if (pwd->pw_uid && !access(_PATH_NOLOGIN, F_OK)) { rshd_errx(1, "Logins currently disabled."); } #else if (lc != NULL && fromp->sa_family == AF_INET) { /*XXX*/ char remote_ip[MAXHOSTNAMELEN]; strncpy(remote_ip, numericname, sizeof(remote_ip) - 1); remote_ip[sizeof(remote_ip) - 1] = 0; /* XXX truncation! */ if (!auth_hostok(lc, rhost, remote_ip)) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", ruser, rhost, luser, __rcmd_errstr, cmdbuf); rshd_errx(1, "Login incorrect."); } if (!auth_timeok(lc, time(NULL))) rshd_errx(1, "Logins not available right now"); } /* * PAM modules might add supplementary groups in * pam_setcred(), so initialize them first. * But we need to open the session as root. */ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { syslog(LOG_ERR, "setusercontext: %m"); exit(1); } #endif /* !__APPLE__ */ #if !TARGET_OS_EMBEDDED if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, pam_err)); } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err)); } #endif (void) write(STDERR_FILENO, "\0", 1); sent_null = 1; if (port) { if (pipe(pv) < 0) rshd_errx(1, "Can't make pipe."); #if defined(KERBEROS) && defined(CRYPT) if (doencrypt) { if (pipe(pv1) < 0) rshd_errx(1, "Can't make 2nd pipe."); if (pipe(pv2) < 0) rshd_errx(1, "Can't make 3rd pipe."); } #endif /* KERBEROS && CRYPT */ pid = fork(); if (pid == -1) rshd_errx(1, "Can't fork; try again."); if (pid) { #if defined(KERBEROS) && defined(CRYPT) if (doencrypt) { static char msg[] = SECURE_MESSAGE; (void) close(pv1[1]); (void) close(pv2[1]); des_write(s, msg, sizeof(msg) - 1); } else #endif /* KERBEROS && CRYPT */ (void) close(0); (void) close(1); (void) close(2); (void) close(pv[1]); FD_ZERO(&readfrom); FD_SET(s, &readfrom); FD_SET(pv[0], &readfrom); if (pv[0] > s) nfd = pv[0]; else nfd = s; #if defined(KERBEROS) && defined(CRYPT) if (doencrypt) { FD_ZERO(&writeto); FD_SET(pv2[0], &writeto); FD_SET(pv1[0], &readfrom); nfd = MAX(nfd, pv2[0]); nfd = MAX(nfd, pv1[0]); } else #endif /* KERBEROS && CRYPT */ ioctl(pv[0], FIONBIO, (char *)&one); /* should set s nbio! */ nfd++; do { ready = readfrom; #if defined(KERBEROS) && defined(CRYPT) if (doencrypt) { wready = writeto; if (select(nfd, &ready, &wready, (fd_set *) 0, (struct timeval *) 0) < 0) break; } else #endif /* KERBEROS && CRYPT */ if (select(nfd, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)0) < 0) break; if (FD_ISSET(s, &ready)) { int ret; #if defined(KERBEROS) && defined(CRYPT) if (doencrypt) ret = des_read(s, &sig, 1); else #endif /* KERBEROS && CRYPT */ ret = read(s, &sig, 1); if (ret <= 0) FD_CLR(s, &readfrom); else killpg(pid, sig); } if (FD_ISSET(pv[0], &ready)) { errno = 0; cc = read(pv[0], buf, sizeof(buf)); if (cc <= 0) { shutdown(s, SHUT_RDWR); FD_CLR(pv[0], &readfrom); } else { #if defined(KERBEROS) && defined(CRYPT) if (doencrypt) (void) des_write(s, buf, cc); else #endif /* KERBEROS && CRYPT */ (void)write(s, buf, cc); } } #if defined(KERBEROS) && defined(CRYPT) if (doencrypt && FD_ISSET(pv1[0], &ready)) { errno = 0; cc = read(pv1[0], buf, sizeof(buf)); if (cc <= 0) { shutdown(pv1[0], 1+1); FD_CLR(pv1[0], &readfrom); } else (void) des_write(STDOUT_FILENO, buf, cc); } if (doencrypt && FD_ISSET(pv2[0], &wready)) { errno = 0; cc = des_read(STDIN_FILENO, buf, sizeof(buf)); if (cc <= 0) { shutdown(pv2[0], 1+1); FD_CLR(pv2[0], &writeto); } else (void) write(pv2[0], buf, cc); } #endif /* KERBEROS && CRYPT */ } while (FD_ISSET(s, &readfrom) || #if defined(KERBEROS) && defined(CRYPT) (doencrypt && FD_ISSET(pv1[0], &readfrom)) || #endif /* KERBEROS && CRYPT */ FD_ISSET(pv[0], &readfrom)); #if !TARGET_OS_EMBEDDED PAM_END; #endif exit(0); } #ifdef __APPLE__ // rdar://problem/4485794 setpgid(0, getpid()); #endif (void) close(s); (void) close(pv[0]); #if defined(KERBEROS) && defined(CRYPT) if (doencrypt) { close(pv1[0]); close(pv2[0]); dup2(pv1[1], 1); dup2(pv2[1], 0); close(pv1[1]); close(pv2[1]); } #endif /* KERBEROS && CRYPT */ dup2(pv[1], 2); close(pv[1]); } #ifndef __APPLE__ else { pid = fork(); if (pid == -1) rshd_errx(1, "Can't fork; try again."); if (pid) { /* Parent. */ while (wait(NULL) > 0 || errno == EINTR) /* nothing */ ; PAM_END; exit(0); } } #endif for (fd = getdtablesize(); fd > 2; fd--) { #ifdef __APPLE__ (void) fcntl(fd, F_SETFD, FD_CLOEXEC); #else (void) close(fd); #endif } if (setsid() == -1) syslog(LOG_ERR, "setsid() failed: %m"); if (setlogin(pwd->pw_name) < 0) syslog(LOG_ERR, "setlogin() failed: %m"); if (*pwd->pw_shell == '\0') pwd->pw_shell = bshell; #ifdef __APPLE__ (void) setgid((gid_t)pwd->pw_gid); initgroups(pwd->pw_name, pwd->pw_gid); (void) setuid((uid_t)pwd->pw_uid); environ = envinit; strncat(homedir, pwd->pw_dir, sizeof(homedir)-6); strcat(path, _PATH_DEFPATH); strncat(shell, pwd->pw_shell, sizeof(shell)-7); strncat(username, pwd->pw_name, sizeof(username)-6); #endif #if !TARGET_OS_EMBEDDED (void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1); (void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1); (void) pam_setenv(pamh, "USER", pwd->pw_name, 1); (void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1); environ = pam_getenvlist(pamh); (void) pam_end(pamh, pam_err); #endif cp = strrchr(pwd->pw_shell, '/'); if (cp) cp++; else cp = pwd->pw_shell; #ifndef __APPLE__ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) { syslog(LOG_ERR, "setusercontext(): %m"); exit(1); } login_close(lc); #endif endpwent(); if (log_success || pwd->pw_uid == 0) { #if defined(KERBEROS) if (use_kerberos) syslog(LOG_INFO|LOG_AUTH, "Kerberos shell from %s.%s@%s on %s as %s, cmd='%.80s'", kdata->pname, kdata->pinst, kdata->prealm, hostname, luser, cmdbuf); else #endif /* KERBEROS */ syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", ruser, rhost, luser, cmdbuf); } execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)NULL); err(1, "%s", pwd->pw_shell); exit(1); }
int main(int argc, char *argv[]) { char *p, *cls = NULL; char *cleanenv[1]; struct passwd * pwd = NULL; int rcswhich, shelltype; int i, num_limits = 0; int ch, doeval = 0, doall = 0; int rtrn, setproc; login_cap_t * lc = NULL; enum { ANY=0, SOFT=1, HARD=2, BOTH=3, DISPLAYONLY=4 } type = ANY; enum { RCSUNKNOWN=0, RCSSET=1, RCSSEL=2 } todo = RCSUNKNOWN; int which_limits[RLIM_NLIMITS]; rlim_t set_limits[RLIM_NLIMITS]; struct rlimit limits[RLIM_NLIMITS]; pid_t pid; /* init resource tables */ for (i = 0; i < RLIM_NLIMITS; i++) { which_limits[i] = 0; /* Don't set/display any */ set_limits[i] = RLIM_INFINITY; } pid = -1; optarg = NULL; while ((ch = getopt(argc, argv, ":EeC:U:BSHP:ab:c:d:f:l:m:n:s:t:u:v:p:w:k:o:")) != -1) { switch(ch) { case 'a': doall = 1; break; case 'E': environ = cleanenv; cleanenv[0] = NULL; break; case 'e': doeval = 1; break; case 'C': cls = optarg; break; case 'U': if ((pwd = getpwnam(optarg)) == NULL) { if (!isdigit(*optarg) || (pwd = getpwuid(atoi(optarg))) == NULL) { warnx("invalid user `%s'", optarg); usage(); } } break; case 'H': type = HARD; break; case 'S': type = SOFT; break; case 'B': type = SOFT|HARD; break; case 'P': if (!isdigit(*optarg) || (pid = atoi(optarg)) < 0) { warnx("invalid pid `%s'", optarg); usage(); } break; default: case ':': /* Without arg */ if ((p = strchr(rcs_string, optopt)) != NULL) { int rcswhich1 = p - rcs_string; if (optarg && *optarg == '-') { /* 'arg' is actually a switch */ --optind; /* back one arg, and make arg NULL */ optarg = NULL; } todo = optarg == NULL ? RCSSEL : RCSSET; if (type == ANY) type = BOTH; which_limits[rcswhich1] = optarg ? type : DISPLAYONLY; set_limits[rcswhich1] = resource_num(rcswhich1, optopt, optarg); num_limits++; break; } /* FALLTHRU */ case '?': usage(); } optarg = NULL; } if (pid != -1) { if (cls != NULL) { warnx("-C cannot be used with -P option"); usage(); } if (pwd != NULL) { warnx("-U cannot be used with -P option"); usage(); } } /* Get current resource values */ setproc = 0; for (i = 0; i < RLIM_NLIMITS; i++) { if (pid == -1) { getrlimit(i, &limits[i]); } else if (doall || num_limits == 0) { getrlimit_proc(pid, i, &limits[i]); } else if (which_limits[i] != 0) { getrlimit_proc(pid, i, &limits[i]); setproc = 1; } } /* If user was specified, get class from that */ if (pwd != NULL) lc = login_getpwclass(pwd); else if (cls != NULL && *cls != '\0') { lc = login_getclassbyname(cls, NULL); if (lc == NULL || strcmp(cls, lc->lc_class) != 0) fprintf(stderr, "login class '%s' non-existent, using %s\n", cls, lc?lc->lc_class:"current settings"); } /* If we have a login class, update resource table from that */ if (lc != NULL) { for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { char str[40]; rlim_t val; /* current value overridden by resourcename or resourcename-cur */ sprintf(str, "%s-cur", resources[rcswhich].cap); val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_cur, limits[rcswhich].rlim_cur); limits[rcswhich].rlim_cur = resources[rcswhich].func(lc, str, val, val); /* maximum value overridden by resourcename or resourcename-max */ sprintf(str, "%s-max", resources[rcswhich].cap); val = resources[rcswhich].func(lc, resources[rcswhich].cap, limits[rcswhich].rlim_max, limits[rcswhich].rlim_max); limits[rcswhich].rlim_max = resources[rcswhich].func(lc, str, val, val); } } /* now, let's determine what we wish to do with all this */ argv += optind; /* If we're setting limits or doing an eval (ie. we're not just * displaying), then check that hard limits are not lower than * soft limits, and force rasing the hard limit if we need to if * we are raising the soft limit, or lower the soft limit if we * are lowering the hard limit. */ if ((*argv || doeval) && getuid() == 0) { for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { if (limits[rcswhich].rlim_max != RLIM_INFINITY) { if (limits[rcswhich].rlim_cur == RLIM_INFINITY) { limits[rcswhich].rlim_max = RLIM_INFINITY; which_limits[rcswhich] |= HARD; } else if (limits[rcswhich].rlim_cur > limits[rcswhich].rlim_max) { if (which_limits[rcswhich] == SOFT) { limits[rcswhich].rlim_max = limits[rcswhich].rlim_cur; which_limits[rcswhich] |= HARD; } else if (which_limits[rcswhich] == HARD) { limits[rcswhich].rlim_cur = limits[rcswhich].rlim_max; which_limits[rcswhich] |= SOFT; } else { /* else.. if we're specifically setting both to * silly values, then let it error out. */ } } } } } /* See if we've overridden anything specific on the command line */ if (num_limits && todo == RCSSET) { for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { if (which_limits[rcswhich] & HARD) limits[rcswhich].rlim_max = set_limits[rcswhich]; if (which_limits[rcswhich] & SOFT) limits[rcswhich].rlim_cur = set_limits[rcswhich]; } } /* If *argv is not NULL, then we are being asked to * (perhaps) set environment variables and run a program */ if (*argv) { if (doeval) { warnx("-e cannot be used with `cmd' option"); usage(); } if (pid != -1) { warnx("-P cannot be used with `cmd' option"); usage(); } login_close(lc); /* set leading environment variables, like eval(1) */ while (*argv && (p = strchr(*argv, '='))) { *p = '\0'; rtrn = setenv(*argv++, p + 1, 1); *p = '='; if (rtrn == -1) err(EXIT_FAILURE, "setenv %s", *argv); } /* Set limits */ for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { if (doall || num_limits == 0 || which_limits[rcswhich] != 0) if (setrlimit(rcswhich, &limits[rcswhich]) == -1) err(1, "setrlimit %s", resources[rcswhich].cap); } if (*argv == NULL) usage(); execvp(*argv, argv); err(1, "%s", *argv); } if (setproc) { for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { if (which_limits[rcswhich] != 0) setrlimit_proc(pid, rcswhich, &limits[rcswhich]); } exit(EXIT_SUCCESS); } shelltype = doeval ? getshelltype() : SH_NONE; if (type == ANY) /* Default to soft limits */ type = SOFT; /* Display limits */ printf(shellparm[shelltype].cmd, lc ? " for class " : " (current)", lc ? lc->lc_class : ""); for (rcswhich = 0; rcswhich < RLIM_NLIMITS; rcswhich++) { if (doall || num_limits == 0 || which_limits[rcswhich] != 0) { if (which_limits[rcswhich] == ANY || which_limits[rcswhich]) which_limits[rcswhich] = type; if (shellparm[shelltype].lprm[rcswhich].pfx) { if (shellparm[shelltype].both && limits[rcswhich].rlim_cur == limits[rcswhich].rlim_max) { print_limit(limits[rcswhich].rlim_max, shellparm[shelltype].lprm[rcswhich].divisor, shellparm[shelltype].inf, shellparm[shelltype].lprm[rcswhich].pfx, shellparm[shelltype].lprm[rcswhich].sfx, shellparm[shelltype].both); } else { if (which_limits[rcswhich] & HARD) { print_limit(limits[rcswhich].rlim_max, shellparm[shelltype].lprm[rcswhich].divisor, shellparm[shelltype].inf, shellparm[shelltype].lprm[rcswhich].pfx, shellparm[shelltype].lprm[rcswhich].sfx, shellparm[shelltype].hard); } if (which_limits[rcswhich] & SOFT) { print_limit(limits[rcswhich].rlim_cur, shellparm[shelltype].lprm[rcswhich].divisor, shellparm[shelltype].inf, shellparm[shelltype].lprm[rcswhich].pfx, shellparm[shelltype].lprm[rcswhich].sfx, shellparm[shelltype].soft); } } } } } login_close(lc); exit(EXIT_SUCCESS); }
void doit(struct sockaddr *fromp) { extern char *__rcmd_errstr; /* syslog hook from libc/net/rcmd.c. */ struct passwd *pwd; u_short port; fd_set ready, readfrom; int cc, fd, nfd, pv[2], pid, s; int one = 1; const char *cp, *errorstr; char sig, buf[BUFSIZ]; char *cmdbuf, luser[16], ruser[16]; char rhost[2 * MAXHOSTNAMELEN + 1]; char numericname[INET6_ADDRSTRLEN]; int af, srcport; int maxcmdlen; login_cap_t *lc; maxcmdlen = (int)sysconf(_SC_ARG_MAX); if (maxcmdlen <= 0 || (cmdbuf = malloc(maxcmdlen)) == NULL) exit(1); (void) signal(SIGINT, SIG_DFL); (void) signal(SIGQUIT, SIG_DFL); (void) signal(SIGTERM, SIG_DFL); af = fromp->sa_family; srcport = ntohs(*((in_port_t *)&fromp->sa_data)); if (af == AF_INET) { inet_ntop(af, &((struct sockaddr_in *)fromp)->sin_addr, numericname, sizeof numericname); } else if (af == AF_INET6) { inet_ntop(af, &((struct sockaddr_in6 *)fromp)->sin6_addr, numericname, sizeof numericname); } else { syslog(LOG_ERR, "malformed \"from\" address (af %d)", af); exit(1); } #ifdef IP_OPTIONS if (af == AF_INET) { u_char optbuf[BUFSIZ/3]; socklen_t optsize = sizeof(optbuf), ipproto, i; struct protoent *ip; if ((ip = getprotobyname("ip")) != NULL) ipproto = ip->p_proto; else ipproto = IPPROTO_IP; if (!getsockopt(0, ipproto, IP_OPTIONS, optbuf, &optsize) && optsize != 0) { for (i = 0; i < optsize; ) { u_char c = optbuf[i]; if (c == IPOPT_LSRR || c == IPOPT_SSRR) { syslog(LOG_NOTICE, "connection refused from %s with IP option %s", numericname, c == IPOPT_LSRR ? "LSRR" : "SSRR"); exit(1); } if (c == IPOPT_EOL) break; i += (c == IPOPT_NOP) ? 1 : optbuf[i+1]; } } } #endif if (srcport >= IPPORT_RESERVED || srcport < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "connection from %s on illegal port %u", numericname, srcport); exit(1); } (void) alarm(60); port = 0; s = 0; /* not set or used if port == 0 */ for (;;) { char c; if ((cc = read(STDIN_FILENO, &c, 1)) != 1) { if (cc < 0) syslog(LOG_NOTICE, "read: %m"); shutdown(0, SHUT_RDWR); exit(1); } if (c == 0) break; port = port * 10 + c - '0'; } (void) alarm(0); if (port != 0) { int lport = IPPORT_RESERVED - 1; s = rresvport_af(&lport, af); if (s < 0) { syslog(LOG_ERR, "can't get stderr port: %m"); exit(1); } if (port >= IPPORT_RESERVED || port < IPPORT_RESERVED/2) { syslog(LOG_NOTICE|LOG_AUTH, "2nd socket from %s on unreserved port %u", numericname, port); exit(1); } *((in_port_t *)&fromp->sa_data) = htons(port); if (connect(s, fromp, fromp->sa_len) < 0) { syslog(LOG_INFO, "connect second port %d: %m", port); exit(1); } } errorstr = NULL; realhostname_sa(rhost, sizeof(rhost) - 1, fromp, fromp->sa_len); rhost[sizeof(rhost) - 1] = '\0'; /* XXX truncation! */ (void) alarm(60); getstr(ruser, sizeof(ruser), "ruser"); getstr(luser, sizeof(luser), "luser"); getstr(cmdbuf, maxcmdlen, "command"); (void) alarm(0); pam_err = pam_start("rsh", luser, &pamc, &pamh); if (pam_err != PAM_SUCCESS) { syslog(LOG_ERR|LOG_AUTH, "pam_start(): %s", pam_strerror(pamh, pam_err)); rshd_errx(1, "Login incorrect."); } if ((pam_err = pam_set_item(pamh, PAM_RUSER, ruser)) != PAM_SUCCESS || (pam_err = pam_set_item(pamh, PAM_RHOST, rhost)) != PAM_SUCCESS) { syslog(LOG_ERR|LOG_AUTH, "pam_set_item(): %s", pam_strerror(pamh, pam_err)); rshd_errx(1, "Login incorrect."); } pam_err = pam_authenticate(pamh, 0); if (pam_err == PAM_SUCCESS) { if ((pam_err = pam_get_user(pamh, &cp, NULL)) == PAM_SUCCESS) { strncpy(luser, cp, sizeof(luser)); luser[sizeof(luser) - 1] = '\0'; /* XXX truncation! */ } pam_err = pam_acct_mgmt(pamh, 0); } if (pam_err != PAM_SUCCESS) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", ruser, rhost, luser, pam_strerror(pamh, pam_err), cmdbuf); rshd_errx(1, "Login incorrect."); } setpwent(); pwd = getpwnam(luser); if (pwd == NULL) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: unknown login. cmd='%.80s'", ruser, rhost, luser, cmdbuf); if (errorstr == NULL) errorstr = "Login incorrect."; rshd_errx(1, errorstr, rhost); } lc = login_getpwclass(pwd); if (pwd->pw_uid) auth_checknologin(lc); if (chdir(pwd->pw_dir) < 0) { if (chdir("/") < 0 || login_getcapbool(lc, "requirehome", !!pwd->pw_uid)) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: no home directory. cmd='%.80s'", ruser, rhost, luser, cmdbuf); rshd_errx(0, "No remote home directory."); } pwd->pw_dir = slash; } if (lc != NULL && fromp->sa_family == AF_INET) { /*XXX*/ char remote_ip[MAXHOSTNAMELEN]; strncpy(remote_ip, numericname, sizeof(remote_ip) - 1); remote_ip[sizeof(remote_ip) - 1] = 0; /* XXX truncation! */ if (!auth_hostok(lc, rhost, remote_ip)) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: permission denied (%s). cmd='%.80s'", ruser, rhost, luser, __rcmd_errstr, cmdbuf); rshd_errx(1, "Login incorrect."); } if (!auth_timeok(lc, time(NULL))) rshd_errx(1, "Logins not available right now"); } /* * PAM modules might add supplementary groups in * pam_setcred(), so initialize them first. * But we need to open the session as root. */ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { syslog(LOG_ERR, "setusercontext: %m"); exit(1); } if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_open_session: %s", pam_strerror(pamh, pam_err)); } else if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS) { syslog(LOG_ERR, "pam_setcred: %s", pam_strerror(pamh, pam_err)); } (void) write(STDERR_FILENO, "\0", 1); sent_null = 1; if (port) { if (pipe(pv) < 0) rshd_errx(1, "Can't make pipe."); pid = fork(); if (pid == -1) rshd_errx(1, "Can't fork; try again."); if (pid) { (void) close(0); (void) close(1); (void) close(2); (void) close(pv[1]); FD_ZERO(&readfrom); FD_SET(s, &readfrom); FD_SET(pv[0], &readfrom); if (pv[0] > s) nfd = pv[0]; else nfd = s; ioctl(pv[0], FIONBIO, (char *)&one); /* should set s nbio! */ nfd++; do { ready = readfrom; if (select(nfd, &ready, (fd_set *)0, (fd_set *)0, (struct timeval *)0) < 0) break; if (FD_ISSET(s, &ready)) { int ret; ret = read(s, &sig, 1); if (ret <= 0) FD_CLR(s, &readfrom); else killpg(pid, sig); } if (FD_ISSET(pv[0], &ready)) { errno = 0; cc = read(pv[0], buf, sizeof(buf)); if (cc <= 0) { shutdown(s, SHUT_RDWR); FD_CLR(pv[0], &readfrom); } else { (void)write(s, buf, cc); } } } while (FD_ISSET(s, &readfrom) || FD_ISSET(pv[0], &readfrom)); PAM_END; exit(0); } (void) close(s); (void) close(pv[0]); dup2(pv[1], 2); close(pv[1]); } else { pid = fork(); if (pid == -1) rshd_errx(1, "Can't fork; try again."); if (pid) { /* Parent. */ while (wait(NULL) > 0 || errno == EINTR) /* nothing */ ; PAM_END; exit(0); } } for (fd = getdtablesize(); fd > 2; fd--) (void) close(fd); if (setsid() == -1) syslog(LOG_ERR, "setsid() failed: %m"); if (setlogin(pwd->pw_name) < 0) syslog(LOG_ERR, "setlogin() failed: %m"); if (*pwd->pw_shell == '\0') pwd->pw_shell = bshell; (void) pam_setenv(pamh, "HOME", pwd->pw_dir, 1); (void) pam_setenv(pamh, "SHELL", pwd->pw_shell, 1); (void) pam_setenv(pamh, "USER", pwd->pw_name, 1); (void) pam_setenv(pamh, "PATH", _PATH_DEFPATH, 1); environ = pam_getenvlist(pamh); (void) pam_end(pamh, pam_err); cp = strrchr(pwd->pw_shell, '/'); if (cp) cp++; else cp = pwd->pw_shell; if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~LOGIN_SETGROUP) < 0) { syslog(LOG_ERR, "setusercontext(): %m"); exit(1); } login_close(lc); endpwent(); if (log_success || pwd->pw_uid == 0) { syslog(LOG_INFO|LOG_AUTH, "%s@%s as %s: cmd='%.80s'", ruser, rhost, luser, cmdbuf); } execl(pwd->pw_shell, cp, "-c", cmdbuf, (char *)NULL); err(1, "%s", pwd->pw_shell); exit(1); }
int main(int argc, char **argv) { struct group *gr; struct stat st; int retries, backoff; int ask, ch, cnt, quietlog, rootlogin, rval; uid_t uid, euid; gid_t egid; char *term; char *p, *ttyn; char tname[sizeof(_PATH_TTY) + 10]; char *arg0; const char *tp; const char *shell = NULL; login_cap_t *lc = NULL; login_cap_t *lc_user = NULL; pid_t pid; #ifdef USE_BSM_AUDIT char auditsuccess = 1; #endif signal(SIGQUIT, SIG_IGN); signal(SIGINT, SIG_IGN); signal(SIGHUP, SIG_IGN); if (setjmp(timeout_buf)) { if (failures) badlogin(username); fprintf(stderr, "Login timed out after %d seconds\n", timeout); bail(NO_SLEEP_EXIT, 0); } signal(SIGALRM, timedout); alarm(timeout); setpriority(PRIO_PROCESS, 0, 0); openlog("login", LOG_ODELAY, LOG_AUTH); uid = getuid(); euid = geteuid(); egid = getegid(); while ((ch = getopt(argc, argv, "fh:p")) != -1) switch (ch) { case 'f': fflag = 1; break; case 'h': if (uid != 0) errx(1, "-h option: %s", strerror(EPERM)); if (strlen(optarg) >= MAXHOSTNAMELEN) errx(1, "-h option: %s: exceeds maximum " "hostname size", optarg); hflag = 1; hostname = optarg; break; case 'p': pflag = 1; break; case '?': default: if (uid == 0) syslog(LOG_ERR, "invalid flag %c", ch); usage(); } argc -= optind; argv += optind; if (argc > 0) { username = strdup(*argv); if (username == NULL) err(1, "strdup()"); ask = 0; } else { ask = 1; } setproctitle("-%s", getprogname()); for (cnt = getdtablesize(); cnt > 2; cnt--) close(cnt); /* * Get current TTY */ ttyn = ttyname(STDIN_FILENO); if (ttyn == NULL || *ttyn == '\0') { snprintf(tname, sizeof(tname), "%s??", _PATH_TTY); ttyn = tname; } if (strncmp(ttyn, _PATH_DEV, sizeof(_PATH_DEV) -1) == 0) tty = ttyn + sizeof(_PATH_DEV) -1; else tty = ttyn; /* * Get "login-retries" & "login-backoff" from default class */ lc = login_getclass(NULL); prompt = login_getcapstr(lc, "login_prompt", default_prompt, default_prompt); passwd_prompt = login_getcapstr(lc, "passwd_prompt", default_passwd_prompt, default_passwd_prompt); retries = login_getcapnum(lc, "login-retries", DEFAULT_RETRIES, DEFAULT_RETRIES); backoff = login_getcapnum(lc, "login-backoff", DEFAULT_BACKOFF, DEFAULT_BACKOFF); login_close(lc); lc = NULL; /* * Try to authenticate the user until we succeed or time out. */ for (cnt = 0;; ask = 1) { if (ask) { fflag = 0; if (olduser != NULL) free(olduser); olduser = username; username = getloginname(); } rootlogin = 0; /* * Note if trying multiple user names; log failures for * previous user name, but don't bother logging one failure * for nonexistent name (mistyped username). */ if (failures && strcmp(olduser, username) != 0) { if (failures > (pwd ? 0 : 1)) badlogin(olduser); } /* * Load the PAM policy and set some variables */ pam_err = pam_start("login", username, &pamc, &pamh); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_start()"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pam_err = pam_set_item(pamh, PAM_TTY, tty); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_set_item(PAM_TTY)"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pam_err = pam_set_item(pamh, PAM_RHOST, hostname); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_set_item(PAM_RHOST)"); #ifdef USE_BSM_AUDIT au_login_fail("PAM Error", 1); #endif bail(NO_SLEEP_EXIT, 1); } pwd = getpwnam(username); if (pwd != NULL && pwd->pw_uid == 0) rootlogin = 1; /* * If the -f option was specified and the caller is * root or the caller isn't changing their uid, don't * authenticate. */ if (pwd != NULL && fflag && (uid == (uid_t)0 || uid == (uid_t)pwd->pw_uid)) { /* already authenticated */ rval = 0; #ifdef USE_BSM_AUDIT auditsuccess = 0; /* opened a terminal window only */ #endif } else { fflag = 0; setpriority(PRIO_PROCESS, 0, -4); rval = auth_pam(); setpriority(PRIO_PROCESS, 0, 0); } if (pwd && rval == 0) break; pam_cleanup(); /* * We are not exiting here, but this corresponds to a failed * login event, so set exitstatus to 1. */ #ifdef USE_BSM_AUDIT au_login_fail("Login incorrect", 1); #endif printf("Login incorrect\n"); failures++; pwd = NULL; /* * Allow up to 'retry' (10) attempts, but start * backing off after 'backoff' (3) attempts. */ if (++cnt > backoff) { if (cnt >= retries) { badlogin(username); bail(SLEEP_EXIT, 1); } sleep((u_int)((cnt - backoff) * 5)); } } /* committed to login -- turn off timeout */ alarm((u_int)0); signal(SIGHUP, SIG_DFL); endpwent(); #ifdef USE_BSM_AUDIT /* Audit successful login. */ if (auditsuccess) au_login_success(); #endif /* * Establish the login class. */ lc = login_getpwclass(pwd); lc_user = login_getuserclass(pwd); if (!(quietlog = login_getcapbool(lc_user, "hushlogin", 0))) quietlog = login_getcapbool(lc, "hushlogin", 0); /* * Switching needed for NFS with root access disabled. * * XXX: This change fails to modify the additional groups for the * process, and as such, may restrict rights normally granted * through those groups. */ setegid(pwd->pw_gid); seteuid(rootlogin ? 0 : pwd->pw_uid); if (!*pwd->pw_dir || chdir(pwd->pw_dir) < 0) { if (login_getcapbool(lc, "requirehome", 0)) refused("Home directory not available", "HOMEDIR", 1); if (chdir("/") < 0) refused("Cannot find root directory", "ROOTDIR", 1); if (!quietlog || *pwd->pw_dir) printf("No home directory.\nLogging in with home = \"/\".\n"); pwd->pw_dir = strdup("/"); if (pwd->pw_dir == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } } seteuid(euid); setegid(egid); if (!quietlog) { quietlog = access(_PATH_HUSHLOGIN, F_OK) == 0; if (!quietlog) pam_silent = 0; } shell = login_getcapstr(lc, "shell", pwd->pw_shell, pwd->pw_shell); if (*pwd->pw_shell == '\0') pwd->pw_shell = strdup(_PATH_BSHELL); if (pwd->pw_shell == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } if (*shell == '\0') /* Not overridden */ shell = pwd->pw_shell; if ((shell = strdup(shell)) == NULL) { syslog(LOG_NOTICE, "strdup(): %m"); bail(SLEEP_EXIT, 1); } /* * Set device protections, depending on what terminal the * user is logged in. This feature is used on Suns to give * console users better privacy. */ login_fbtab(tty, pwd->pw_uid, pwd->pw_gid); /* * Clear flags of the tty. None should be set, and when the * user sets them otherwise, this can cause the chown to fail. * Since it isn't clear that flags are useful on character * devices, we just clear them. * * We don't log in the case of EOPNOTSUPP because dev might be * on NFS, which doesn't support chflags. * * We don't log in the EROFS because that means that /dev is on * a read only file system and we assume that the permissions there * are sane. */ if (ttyn != tname && chflags(ttyn, 0)) if (errno != EOPNOTSUPP && errno != EROFS) syslog(LOG_ERR, "chflags(%s): %m", ttyn); if (ttyn != tname && chown(ttyn, pwd->pw_uid, (gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid)) if (errno != EROFS) syslog(LOG_ERR, "chown(%s): %m", ttyn); /* * Exclude cons/vt/ptys only, assume dialup otherwise * TODO: Make dialup tty determination a library call * for consistency (finger etc.) */ if (hflag && isdialuptty(tty)) syslog(LOG_INFO, "DIALUP %s, %s", tty, pwd->pw_name); #ifdef LOGALL /* * Syslog each successful login, so we don't have to watch * hundreds of wtmp or lastlogin files. */ if (hflag) syslog(LOG_INFO, "login from %s on %s as %s", hostname, tty, pwd->pw_name); else syslog(LOG_INFO, "login on %s as %s", tty, pwd->pw_name); #endif /* * If fflag is on, assume caller/authenticator has logged root * login. */ if (rootlogin && fflag == 0) { if (hflag) syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s FROM %s", username, tty, hostname); else syslog(LOG_NOTICE, "ROOT LOGIN (%s) ON %s", username, tty); } /* * Destroy environment unless user has requested its * preservation - but preserve TERM in all cases */ term = getenv("TERM"); if (!pflag) environ = envinit; if (term != NULL) { if (setenv("TERM", term, 0) == -1) err(1, "setenv: cannot set TERM=%s", term); } /* * PAM modules might add supplementary groups during pam_setcred(). */ if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETGROUP) != 0) { syslog(LOG_ERR, "setusercontext() failed - exiting"); bail(NO_SLEEP_EXIT, 1); } pam_err = pam_setcred(pamh, pam_silent|PAM_ESTABLISH_CRED); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_setcred()"); bail(NO_SLEEP_EXIT, 1); } pam_cred_established = 1; pam_err = pam_open_session(pamh, pam_silent); if (pam_err != PAM_SUCCESS) { pam_syslog("pam_open_session()"); bail(NO_SLEEP_EXIT, 1); } pam_session_established = 1; /* * We must fork() before setuid() because we need to call * pam_close_session() as root. */ pid = fork(); if (pid < 0) { err(1, "fork"); } else if (pid != 0) { /* * Parent: wait for child to finish, then clean up * session. */ int status; setproctitle("-%s [pam]", getprogname()); waitpid(pid, &status, 0); bail(NO_SLEEP_EXIT, 0); } /* * NOTICE: We are now in the child process! */ /* * Add any environment variables the PAM modules may have set. */ export_pam_environment(); /* * We're done with PAM now; our parent will deal with the rest. */ pam_end(pamh, 0); pamh = NULL; /* * We don't need to be root anymore, so set the login name and * the UID. */ if (setlogin(username) != 0) { syslog(LOG_ERR, "setlogin(%s): %m - exiting", username); bail(NO_SLEEP_EXIT, 1); } if (setusercontext(lc, pwd, pwd->pw_uid, LOGIN_SETALL & ~(LOGIN_SETLOGIN|LOGIN_SETGROUP)) != 0) { syslog(LOG_ERR, "setusercontext() failed - exiting"); exit(1); } if (setenv("SHELL", pwd->pw_shell, 1) == -1) err(1, "setenv: cannot set SHELL=%s", pwd->pw_shell); if (setenv("HOME", pwd->pw_dir, 1) == -1) err(1, "setenv: cannot set HOME=%s", pwd->pw_dir); /* Overwrite "term" from login.conf(5) for any known TERM */ if (term == NULL && (tp = stypeof(tty)) != NULL) { if (setenv("TERM", tp, 1) == -1) err(1, "setenv: cannot set TERM=%s", tp); } else { if (setenv("TERM", TERM_UNKNOWN, 0) == -1) err(1, "setenv: cannot set TERM=%s", TERM_UNKNOWN); } if (setenv("LOGNAME", username, 1) == -1) err(1, "setenv: cannot set LOGNAME=%s", username); if (setenv("USER", username, 1) == -1) err(1, "setenv: cannot set USER=%s", username); if (setenv("PATH", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH, 0) == -1) { err(1, "setenv: cannot set PATH=%s", rootlogin ? _PATH_STDPATH : _PATH_DEFPATH); } if (!quietlog) { const char *cw; cw = login_getcapstr(lc, "copyright", NULL, NULL); if (cw == NULL || motd(cw) == -1) printf("%s", copyright); printf("\n"); cw = login_getcapstr(lc, "welcome", NULL, NULL); if (cw != NULL && access(cw, F_OK) == 0) motd(cw); else motd(_PATH_MOTDFILE); if (login_getcapbool(lc_user, "nocheckmail", 0) == 0 && login_getcapbool(lc, "nocheckmail", 0) == 0) { char *cx; /* $MAIL may have been set by class. */ cx = getenv("MAIL"); if (cx == NULL) { asprintf(&cx, "%s/%s", _PATH_MAILDIR, pwd->pw_name); } if (cx && stat(cx, &st) == 0 && st.st_size != 0) printf("You have %smail.\n", (st.st_mtime > st.st_atime) ? "new " : ""); if (getenv("MAIL") == NULL) free(cx); } } login_close(lc_user); login_close(lc); signal(SIGALRM, SIG_DFL); signal(SIGQUIT, SIG_DFL); signal(SIGINT, SIG_DFL); signal(SIGTSTP, SIG_IGN); /* * Login shells have a leading '-' in front of argv[0] */ p = strrchr(pwd->pw_shell, '/'); if (asprintf(&arg0, "-%s", p ? p + 1 : pwd->pw_shell) >= MAXPATHLEN) { syslog(LOG_ERR, "user: %s: shell exceeds maximum pathname size", username); errx(1, "shell exceeds maximum pathname size"); } else if (arg0 == NULL) { err(1, "asprintf()"); } execlp(shell, arg0, NULL); err(1, "%s", shell); /* * That's it, folks! */ }
struct passwd * getpwnamallow(const char *user) { #ifdef HAVE_LOGIN_CAP extern login_cap_t *lc; #ifdef BSD_AUTH auth_session_t *as; #endif #endif struct passwd *pw; struct connection_info *ci = get_connection_info(1, options.use_dns); ci->user = user; parse_server_match_config(&options, ci); #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_setauthdb(user); #endif pw = getpwnam(user); #if defined(_AIX) && defined(HAVE_SETAUTHDB) aix_restoreauthdb(); #endif #ifdef HAVE_CYGWIN /* * Windows usernames are case-insensitive. To avoid later problems * when trying to match the username, the user is only allowed to * login if the username is given in the same case as stored in the * user database. */ if (pw != NULL && strcmp(user, pw->pw_name) != 0) { logit("Login name %.100s does not match stored username %.100s", user, pw->pw_name); pw = NULL; } #endif if (pw == NULL) { BLACKLIST_NOTIFY(BLACKLIST_AUTH_FAIL); logit("Invalid user %.100s from %.100s", user, get_remote_ipaddr()); #ifdef CUSTOM_FAILED_LOGIN record_failed_login(user, get_canonical_hostname(options.use_dns), "ssh"); #endif #ifdef SSH_AUDIT_EVENTS audit_event(SSH_INVALID_USER); #endif /* SSH_AUDIT_EVENTS */ return (NULL); } if (!allowed_user(pw)) return (NULL); #ifdef HAVE_LOGIN_CAP if ((lc = login_getpwclass(pw)) == NULL) { debug("unable to get login class: %s", user); return (NULL); } #ifdef BSD_AUTH if ((as = auth_open()) == NULL || auth_setpwd(as, pw) != 0 || auth_approval(as, lc, pw->pw_name, "ssh") <= 0) { debug("Approval failure for %s", user); pw = NULL; } if (as != NULL) auth_close(as); #endif #endif if (pw != NULL) return (pwcopy(pw)); return (NULL); }
static int pw_set_passwd(struct passwd *pwd, int fd, bool precrypted, bool update) { int b, istty; struct termios t, n; login_cap_t *lc; char line[_PASSWORD_LEN+1]; char *p; if (fd == '-') { if (!pwd->pw_passwd || *pwd->pw_passwd != '*') { pwd->pw_passwd = "*"; /* No access */ return (1); } return (0); } if ((istty = isatty(fd))) { if (tcgetattr(fd, &t) == -1) istty = 0; else { n = t; n.c_lflag &= ~(ECHO); tcsetattr(fd, TCSANOW, &n); printf("%s%spassword for user %s:", update ? "new " : "", precrypted ? "encrypted " : "", pwd->pw_name); fflush(stdout); } } b = read(fd, line, sizeof(line) - 1); if (istty) { /* Restore state */ tcsetattr(fd, TCSANOW, &t); fputc('\n', stdout); fflush(stdout); } if (b < 0) err(EX_IOERR, "-%c file descriptor", precrypted ? 'H' : 'h'); line[b] = '\0'; if ((p = strpbrk(line, "\r\n")) != NULL) *p = '\0'; if (!*line) errx(EX_DATAERR, "empty password read on file descriptor %d", fd); if (precrypted) { if (strchr(line, ':') != NULL) errx(EX_DATAERR, "bad encrypted password"); pwd->pw_passwd = strdup(line); } else { lc = login_getpwclass(pwd); if (lc == NULL || login_setcryptfmt(lc, "sha512", NULL) == NULL) warn("setting crypt(3) format"); login_close(lc); pwd->pw_passwd = pw_pwcrypt(line); } return (1); }
int setusercontext(login_cap_t *lc, const struct passwd *pwd, uid_t uid, unsigned int flags) { quad_t p; mode_t mymask; login_cap_t *llc = NULL; struct rtprio rtp; if (lc == NULL) { if (pwd != NULL && (lc = login_getpwclass(pwd)) != NULL) llc = lc; /* free this when we're done */ } if (flags & LOGIN_SETPATH) pathvars[0].def = uid ? _PATH_DEFPATH : _PATH_STDPATH; /* we need a passwd entry to set these */ if (pwd == NULL) flags &= ~(LOGIN_SETGROUP | LOGIN_SETLOGIN); /* Set the process priority */ if (flags & LOGIN_SETPRIORITY) { p = login_getcapnum(lc, "priority", LOGIN_DEFPRI, LOGIN_DEFPRI); if (p > PRIO_MAX) { rtp.type = RTP_PRIO_IDLE; rtp.prio = p - PRIO_MAX - 1; p = (rtp.prio > RTP_PRIO_MAX) ? 31 : p; if (rtprio(RTP_SET, 0, &rtp)) syslog(LOG_WARNING, "rtprio '%s' (%s): %m", pwd ? pwd->pw_name : "-", lc ? lc->lc_class : LOGIN_DEFCLASS); } else if (p < PRIO_MIN) { rtp.type = RTP_PRIO_REALTIME; rtp.prio = abs(p - PRIO_MIN + RTP_PRIO_MAX); p = (rtp.prio > RTP_PRIO_MAX) ? 1 : p; if (rtprio(RTP_SET, 0, &rtp)) syslog(LOG_WARNING, "rtprio '%s' (%s): %m", pwd ? pwd->pw_name : "-", lc ? lc->lc_class : LOGIN_DEFCLASS); } else { if (setpriority(PRIO_PROCESS, 0, (int)p) != 0) syslog(LOG_WARNING, "setpriority '%s' (%s): %m", pwd ? pwd->pw_name : "-", lc ? lc->lc_class : LOGIN_DEFCLASS); } } /* Setup the user's group permissions */ if (flags & LOGIN_SETGROUP) { if (setgid(pwd->pw_gid) != 0) { syslog(LOG_ERR, "setgid(%lu): %m", (u_long)pwd->pw_gid); login_close(llc); return (-1); } if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) { syslog(LOG_ERR, "initgroups(%s,%lu): %m", pwd->pw_name, (u_long)pwd->pw_gid); login_close(llc); return (-1); } } /* Set the sessions login */ if ((flags & LOGIN_SETLOGIN) && setlogin(pwd->pw_name) != 0) { syslog(LOG_ERR, "setlogin(%s): %m", pwd->pw_name); login_close(llc); return (-1); } mymask = (flags & LOGIN_SETUMASK) ? umask(LOGIN_DEFUMASK) : 0; mymask = setlogincontext(lc, pwd, mymask, flags); login_close(llc); /* This needs to be done after anything that needs root privs */ if ((flags & LOGIN_SETUSER) && setuid(uid) != 0) { syslog(LOG_ERR, "setuid(%lu): %m", (u_long)uid); return (-1); /* Paranoia again */ } /* * Now, we repeat some of the above for the user's private entries */ if (getuid() == uid && (lc = login_getuserclass(pwd)) != NULL) { mymask = setlogincontext(lc, pwd, mymask, flags); login_close(lc); } /* Finally, set any umask we've found */ if (flags & LOGIN_SETUMASK) umask(mymask); return (0); }
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; } }