diff --git a/session.c b/session.c index 7aec15d4208..39bede1d3dd 100644 --- a/session.c +++ b/session.c @@ -832,33 +832,45 @@ check_quietlogin(Session *s, const char *command) return 0; } +/* + * Check that any custom user environment/rc file is "safe" to run, i.e. + * - it belongs to either the superuser or to the logged in user, of which + * the latter must also own the parent directory + * - if it belongs to the logged in user, either the file or its parent dir + * is only accessible to the user + */ static int -safe_user_file(const char *filename, struct passwd *pw) +safe_user_file(const char *filename, uid_t uid) { struct stat st; - char *path; + char *lastsep; + uid_t dirowner; + mode_t dirmode; + /* Gather parent dir owner and mode first */ + lastsep = strrchr(filename, '/'); + *lastsep = '\0'; if (stat(filename, &st) == -1) return 0; - - path = realpath(filename, NULL); - if (strncmp(path, pw->pw_dir, strlen(pw->pw_dir)) == 0) { - if (st.st_uid != pw->pw_uid) { - fprintf(stderr, "Bad owner on %s, ignoring.\n", - path); - return 0; - } - if ((st.st_mode & 077) != 0) { - fprintf(stderr, - "Permissions too open (%3o) on %s, ignoring.\n", - st.st_mode & 0777u, path); - return 0; - } - return 1; - } - if (st.st_uid != 0) { - fprintf(stderr, "%s not root-owned, ignoring.\n", path); + dirowner = st.st_uid; + dirmode = st.st_mode; + /* Then gather actual file info */ + *lastsep = '/'; + if (stat(filename, &st) == -1) + return 0; + + if (st.st_uid != 0 && st.st_uid != uid && st.st_uid != dirowner) { + fprintf(stderr, "Bad owner on %s, ignoring.\n", + filename); return 0; + } + + if (st.st_uid == uid && + (st.st_mode & 077) != 0 && (dirmode & 077) != 0) { + fprintf(stderr, + "Permissions too open on %s or its parent dir, ignoring.\n", + filename); + return 0; } return 1; } @@ -1152,7 +1164,7 @@ do_setup_env(struct ssh *ssh, Session *s, const char *shell) for (i = 0; i < options.num_user_env_files; i++) { userenv = expand_user_file(options.user_env_files[i], pw, "UserEnvironment"); - if (safe_user_file(userenv, pw)) + if (safe_user_file(userenv, pw->pw_uid)) read_environment_file(&env, &envsize, userenv, options.permit_user_env_allowlist); } @@ -1250,7 +1262,7 @@ do_rc_files(struct ssh *ssh, Session *s, const char *shell) for (i = 0; i < options.num_user_rc_files; i++) { user_rc = expand_user_file(options.user_rc_files[i], s->pw, "UserRC"); - if (safe_user_file(user_rc, s->pw)) { + if (safe_user_file(user_rc, s->pw->pw_uid)) { if (xasprintf(&cmd, "%s -c '%s %s'", shell, _PATH_BSHELL, user_rc) == -1) fatal_f("xasprintf: %s", strerror(errno));