VERSION = 1
PATCHLEVEL = 8
-SUBLEVEL = 0
+SUBLEVEL = 1
EXTRAVERSION =
NAME = Unnamed
#endif
};
+#define ZIP_HEADER_LEN 26
+
typedef union {
- uint8_t raw[26];
+ uint8_t raw[ZIP_HEADER_LEN];
struct {
uint16_t version; /* 0-1 */
uint16_t flags; /* 2-3 */
} formatted ATTRIBUTE_PACKED;
} zip_header_t;
+/* Check the offset of the last element, not the length. This leniency
+ * allows for poor packing, whereby the overall struct may be too long,
+ * even though the elements are all in the right place.
+ */
struct BUG_zip_header_must_be_26_bytes {
- char BUG_zip_header_must_be_26_bytes[sizeof(zip_header_t) == 26 ? 1 : -1];
+ char BUG_zip_header_must_be_26_bytes[
+ offsetof(zip_header_t, formatted.extra_len) + 2 ==
+ ZIP_HEADER_LEN ? 1 : -1];
};
#define FIX_ENDIANNESS(zip_header) do { \
bb_error_msg_and_die("invalid zip magic %08X", magic);
/* Read the file header */
- xread(src_fd, zip_header.raw, sizeof(zip_header));
+ xread(src_fd, zip_header.raw, ZIP_HEADER_LEN);
FIX_ENDIANNESS(zip_header);
if ((zip_header.formatted.method != 0) && (zip_header.formatted.method != 8)) {
bb_error_msg_and_die("unsupported method %d", zip_header.formatted.method);
char full_tty[TTYNAME_SIZE];
USE_SELINUX(security_context_t user_sid = NULL;)
USE_FEATURE_UTMP(struct utmp utent;)
- USE_PAM(pam_handle_t *pamh;)
- USE_PAM(int pamret;)
- USE_PAM(const char *failed_msg;)
+#if ENABLE_PAM
+ int pamret;
+ pam_handle_t *pamh;
+ const char *pamuser;
+ const char *failed_msg;
+ struct passwd pwdstruct;
+ char pwdbuf[256];
+#endif
short_tty = full_tty;
username[0] = '\0';
#if ENABLE_PAM
pamret = pam_start("login", username, &conv, &pamh);
if (pamret != PAM_SUCCESS) {
- failed_msg = "pam_start";
+ failed_msg = "start";
goto pam_auth_failed;
}
/* set TTY (so things like securetty work) */
pamret = pam_set_item(pamh, PAM_TTY, short_tty);
if (pamret != PAM_SUCCESS) {
- failed_msg = "pam_set_item(TTY)";
+ failed_msg = "set_item(TTY)";
goto pam_auth_failed;
}
pamret = pam_authenticate(pamh, 0);
if (pamret != PAM_SUCCESS) {
- failed_msg = "pam_authenticate";
+ failed_msg = "authenticate";
goto pam_auth_failed;
/* TODO: or just "goto auth_failed"
* since user seems to enter wrong password
/* check that the account is healthy */
pamret = pam_acct_mgmt(pamh, 0);
if (pamret != PAM_SUCCESS) {
- failed_msg = "account setup";
+ failed_msg = "acct_mgmt";
goto pam_auth_failed;
}
/* read user back */
- {
- const char *pamuser;
- /* gcc: "dereferencing type-punned pointer breaks aliasing rules..."
- * thus we cast to (void*) */
- if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) {
- failed_msg = "pam_get_item(USER)";
- goto pam_auth_failed;
- }
- safe_strncpy(username, pamuser, sizeof(username));
+ pamuser = NULL;
+ /* gcc: "dereferencing type-punned pointer breaks aliasing rules..."
+ * thus we cast to (void*) */
+ if (pam_get_item(pamh, PAM_USER, (void*)&pamuser) != PAM_SUCCESS) {
+ failed_msg = "get_item(USER)";
+ goto pam_auth_failed;
}
- /* If we get here, the user was authenticated, and is
- * granted access. */
- pw = getpwnam(username);
- if (pw)
- break;
- goto auth_failed;
+ if (!pamuser || !pamuser[0])
+ goto auth_failed;
+ safe_strncpy(username, pamuser, sizeof(username));
+ /* Don't use "pw = getpwnam(username);",
+ * PAM is said to be capable of destroying static storage
+ * used by getpwnam(). We are using safe(r) function */
+ pw = NULL;
+ getpwnam_r(username, &pwdstruct, pwdbuf, sizeof(pwdbuf), &pw);
+ if (!pw)
+ goto auth_failed;
+ pamret = pam_open_session(pamh, 0);
+ if (pamret != PAM_SUCCESS) {
+ failed_msg = "open_session";
+ goto pam_auth_failed;
+ }
+ pamret = pam_setcred(pamh, PAM_ESTABLISH_CRED);
+ if (pamret != PAM_SUCCESS) {
+ failed_msg = "setcred";
+ goto pam_auth_failed;
+ }
+ break; /* success, continue login process */
+
pam_auth_failed:
- bb_error_msg("%s failed: %s (%d)", failed_msg, pam_strerror(pamh, pamret), pamret);
+ bb_error_msg("pam_%s call failed: %s (%d)", failed_msg,
+ pam_strerror(pamh, pamret), pamret);
safe_strncpy(username, "UNKNOWN", sizeof(username));
#else /* not PAM */
pw = getpwnam(username);
if (*tail)
(*tail)->m_next = find;
find->m_prev = *tail;
- /*find->m_next = NULL; - xzalloc did it */
+ find->m_next = NULL; /* possibly NOT done by xzalloc! */
if (!*head)
*head = find;
/* make new session and process group */
setsid();
+ /* Restore default signal handling */
+ signal(SIGCHLD, SIG_DFL);
+ signal(SIGPIPE, SIG_DFL);
+
/* open the child's side of the tty. */
/* NB: setsid() disconnects from any previous ctty's. Therefore
* we must open child's side of the tty AFTER setsid! */
/* Uses FILE-based I/O to stdout, but does fflush(stdout),
* so should be safe with vfork.
* I fear, though, that some users will have ridiculously big
- * issue files, and they may block writing to fd 1. */
+ * issue files, and they may block writing to fd 1,
+ * (parent is supposed to read it, but parent waits
+ * for vforked child to exec!) */
print_login_issue(issuefile, NULL);
/* Exec shell / login / whatever */
login_argv[0] = loginpath;
login_argv[1] = NULL;
- execvp(loginpath, (char **)login_argv);
- /* Safer with vfork, and we shouldn't send message
+ /* exec busybox applet (if PREFER_APPLETS=y), if that fails,
+ * exec external program */
+ BB_EXECVP(loginpath, (char **)login_argv);
+ /* _exit is safer with vfork, and we shouldn't send message
* to remote clients anyway */
_exit(1); /*bb_perror_msg_and_die("execv %s", loginpath);*/
}
#else /* !FEATURE_TELNETD_STANDALONE */
-/* Used in main() only, thus exits. */
+/* Used in main() only, thus "return 0" actually is exit(0). */
#define free_session(ts) return 0
#endif
pid_t pid;
struct tsession *ts;
- pid = waitpid(-1, &sig, WNOHANG);
- if (pid > 0) {
+ /* Looping: more than one child may have exited */
+ while (1) {
+ pid = waitpid(-1, NULL, WNOHANG);
+ if (pid <= 0)
+ break;
ts = sessions;
while (ts) {
if (ts->shell_pid == pid) {
ts->shell_pid = -1;
- return;
+ break;
}
ts = ts->next;
}
}
}
-
int telnetd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int telnetd_main(int argc, char **argv)
{
if (!(opt & OPT_FOREGROUND)) {
/* DAEMON_CHDIR_ROOT was giving inconsistent
* behavior with/without -F, -i */
- bb_daemonize_or_rexec(0 /*DAEMON_CHDIR_ROOT*/, argv);
+ bb_daemonize_or_rexec(0 /*was DAEMON_CHDIR_ROOT*/, argv);
}
}
/* Redirect log to syslog early, if needed */
if (opt & OPT_WATCHCHILD)
signal(SIGCHLD, handle_sigchld);
+ else /* prevent dead children from becoming zombies */
+ signal(SIGCHLD, SIG_IGN);
/*
This is how the buffers are used. The arrows indicate the movement
while (ts) {
struct tsession *next = ts->next; /* in case we free ts. */
if (ts->shell_pid == -1) {
- /* Child died ad we detected that */
+ /* Child died and we detected that */
free_session(ts);
} else {
if (ts->size1 > 0) /* can write to pty */
if (!IS_INETD) {
FD_SET(master_fd, &rdfdset);
/* This is needed because free_session() does not
- * take into account master_fd when it finds new
+ * take master_fd into account when it finds new
* maxfd among remaining fd's */
if (master_fd > maxfd)
maxfd = master_fd;
#
-#
CONFIG_HAVE_DOT_CONFIG=y
#
#
# CONFIG_STATIC is not set
# CONFIG_BUILD_LIBBUSYBOX is not set
-# CONFIG_FEATURE_FULL_LIBBUSYBOX is not set
+# CONFIG_FEATURE_INDIVIDUAL is not set
# CONFIG_FEATURE_SHARED_BUSYBOX is not set
CONFIG_LFS=y
-# CONFIG_BUILD_AT_ONCE is not set
#
# Debugging Options
# CONFIG_INSTALL_NO_USR is not set
CONFIG_INSTALL_APPLET_SYMLINKS=y
# CONFIG_INSTALL_APPLET_HARDLINKS is not set
+# CONFIG_INSTALL_APPLET_SCRIPT_WRAPPERS is not set
# CONFIG_INSTALL_APPLET_DONT is not set
+# CONFIG_INSTALL_SH_APPLET_SYMLINK is not set
+# CONFIG_INSTALL_SH_APPLET_HARDLINK is not set
+# CONFIG_INSTALL_SH_APPLET_SCRIPT_WRAPPER is not set
CONFIG_PREFIX="./_install"
#
CONFIG_AR=y
CONFIG_FEATURE_AR_LONG_FILENAMES=y
CONFIG_BUNZIP2=y
+CONFIG_BZIP2=y
CONFIG_CPIO=y
# CONFIG_DPKG is not set
# CONFIG_DPKG_DEB is not set
CONFIG_CLEAR=y
CONFIG_DEALLOCVT=y
CONFIG_DUMPKMAP=y
+CONFIG_KBD_MODE=y
CONFIG_LOADFONT=y
CONFIG_LOADKMAP=y
CONFIG_OPENVT=y
CONFIG_MORE=y
CONFIG_FEATURE_USE_TERMIOS=y
CONFIG_MOUNT=y
+CONFIG_FEATURE_MOUNT_HELPERS=y
CONFIG_FEATURE_MOUNT_NFS=y
CONFIG_FEATURE_MOUNT_CIFS=y
CONFIG_FEATURE_MOUNT_FLAGS=y
CONFIG_FEATURE_FTPGETPUT_LONG_OPTIONS=y
CONFIG_HOSTNAME=y
CONFIG_HTTPD=y
+CONFIG_FEATURE_HTTPD_RANGES=y
CONFIG_FEATURE_HTTPD_USE_SENDFILE=y
# CONFIG_FEATURE_HTTPD_RELOAD_CONFIG_SIGHUP is not set
# CONFIG_FEATURE_HTTPD_SETUID is not set
CONFIG_FEATURE_HTTPD_SET_REMOTE_PORT_TO_ENV=y
CONFIG_FEATURE_HTTPD_ENCODE_URL_STR=y
CONFIG_FEATURE_HTTPD_ERROR_PAGES=y
+CONFIG_FEATURE_HTTPD_PROXY=y
CONFIG_IFCONFIG=y
CONFIG_FEATURE_IFCONFIG_STATUS=y
CONFIG_FEATURE_IFCONFIG_SLIP=y
CONFIG_FEATURE_IP_TUNNEL=y
CONFIG_FEATURE_IP_RULE=y
CONFIG_FEATURE_IP_SHORT_FORMS=y
+CONFIG_FEATURE_IP_RARE_PROTOCOLS=y
CONFIG_IPADDR=y
CONFIG_IPLINK=y
CONFIG_IPROUTE=y
CONFIG_KILLALL=y
CONFIG_KILLALL5=y
CONFIG_NMETER=y
+CONFIG_PGREP=y
CONFIG_PIDOF=y
CONFIG_FEATURE_PIDOF_SINGLE=y
CONFIG_FEATURE_PIDOF_OMIT=y
+CONFIG_PKILL=y
CONFIG_PS=y
CONFIG_FEATURE_PS_WIDE=y
CONFIG_RENICE=y
CONFIG_FEATURE_TOP_CPU_USAGE_PERCENTAGE=y
CONFIG_FEATURE_TOP_CPU_GLOBAL_PERCENTS=y
# CONFIG_FEATURE_TOP_DECIMALS is not set
+CONFIG_FEATURE_TOPMEM=y
CONFIG_UPTIME=y
CONFIG_WATCH=y
# CONFIG_SETENFORCE is not set
# CONFIG_SETFILES is not set
# CONFIG_FEATURE_SETFILES_CHECK_OPTION is not set
+# CONFIG_SETSEBOOL is not set
#
# ipsvd utilities
}
/* len parameter is used only for "is there a timestamp?" check.
- * NB: some callers cheat and supply 0 when they know
- * that there is no timestamp, short-cutting the test. */
+ * NB: some callers cheat and supply len==0 when they know
+ * that there is no timestamp, short-circuiting the test. */
static void timestamp_and_log(int pri, char *msg, int len)
{
char *timestamp;
if (*p == '<') {
/* Parse the magic priority number */
pri = bb_strtou(p + 1, &p, 10);
- if (*p == '>') p++;
- if (pri & ~(LOG_FACMASK | LOG_PRIMASK)) {
+ if (*p == '>')
+ p++;
+ if (pri & ~(LOG_FACMASK | LOG_PRIMASK))
pri = (LOG_USER | LOG_NOTICE);
- }
}
while ((c = *p++)) {
for (;;) {
size_t sz;
-
+ read_again:
sz = safe_read(sock_fd, G.recvbuf, MAX_READ - 1);
- if (sz <= 0) {
- //if (sz == 0)
- // continue; /* EOF from unix socket??? */
+ if (sz < 0) {
bb_perror_msg_and_die("read from /dev/log");
}
+ /* Drop trailing NULs (typically there is one NUL) */
+ while (1) {
+ if (sz == 0)
+ goto read_again;
+ /* man 3 syslog says: "A trailing newline is added when needed".
+ * However, neither glibc nor uclibc do this:
+ * syslog(prio, "test") sends "test\0" to /dev/log,
+ * syslog(prio, "test\n") sends "test\n\0",
+ * IOW: newline is passed verbatim!
+ * I take it to mean that it's syslogd's job
+ * to make those look identical in the log files */
+ if (G.recvbuf[sz-1] && G.recvbuf[sz-1] != '\n')
+ break;
+ sz--;
+ }
+ /* Maybe we need to add '\n' here, not later?
+ * It looks like stock syslogd does send '\n' over network,
+ * but we do not (see sendto below) */
+ G.recvbuf[sz] = '\0'; /* make sure it *is* NUL terminated */
+
/* TODO: maybe suppress duplicates? */
#if ENABLE_FEATURE_REMOTE_LOG
/* We are not modifying log messages in any way before send */
}
}
#endif
- G.recvbuf[sz] = '\0';
split_escape_and_log(G.recvbuf, sz);
} /* for */
}