summaryrefslogtreecommitdiff
path: root/release/src/router/busybox/loginutils
diff options
context:
space:
mode:
Diffstat (limited to 'release/src/router/busybox/loginutils')
-rw-r--r--[-rwxr-xr-x]release/src/router/busybox/loginutils/Config.in296
-rw-r--r--release/src/router/busybox/loginutils/Kbuild19
-rw-r--r--release/src/router/busybox/loginutils/Makefile30
-rwxr-xr-xrelease/src/router/busybox/loginutils/Makefile.in49
-rw-r--r--release/src/router/busybox/loginutils/addgroup.c220
-rw-r--r--release/src/router/busybox/loginutils/adduser.c374
-rw-r--r--release/src/router/busybox/loginutils/chpasswd.c71
-rw-r--r--release/src/router/busybox/loginutils/cryptpw.c117
-rw-r--r--release/src/router/busybox/loginutils/delgroup.c62
-rw-r--r--release/src/router/busybox/loginutils/delline.c113
-rw-r--r--release/src/router/busybox/loginutils/deluser.c92
-rw-r--r--release/src/router/busybox/loginutils/getty.c1278
-rw-r--r--release/src/router/busybox/loginutils/login.c871
-rw-r--r--release/src/router/busybox/loginutils/passwd.c562
-rw-r--r--release/src/router/busybox/loginutils/su.c214
-rw-r--r--release/src/router/busybox/loginutils/sulogin.c253
-rw-r--r--release/src/router/busybox/loginutils/vlock.c248
17 files changed, 2054 insertions, 2815 deletions
diff --git a/release/src/router/busybox/loginutils/Config.in b/release/src/router/busybox/loginutils/Config.in
index 9eb771f0..ddd0c801 100755..100644
--- a/release/src/router/busybox/loginutils/Config.in
+++ b/release/src/router/busybox/loginutils/Config.in
@@ -5,132 +5,292 @@
menu "Login/Password Management Utilities"
-config CONFIG_USE_BB_PWD_GRP
+config FEATURE_SHADOWPASSWDS
+ bool "Support for shadow passwords"
+ default n
+ help
+ Build support for shadow password in /etc/shadow. This file is only
+ readable by root and thus the encrypted passwords are no longer
+ publicly readable.
+
+config USE_BB_PWD_GRP
bool "Use internal password and group functions rather than system functions"
default n
help
- If you leave this disabled, busybox will use the system's password
- and group functions. And if you are using the GNU C library
- (glibc), you will then need to install the /etc/nsswitch.conf
- configuration file and the required /lib/libnss_* libraries in
- order for the password and group functions to work. This generally
- makes your embedded system quite a bit larger.
+ If you leave this disabled, busybox will use the system's password
+ and group functions. And if you are using the GNU C library
+ (glibc), you will then need to install the /etc/nsswitch.conf
+ configuration file and the required /lib/libnss_* libraries in
+ order for the password and group functions to work. This generally
+ makes your embedded system quite a bit larger.
+
+ Enabling this option will cause busybox to directly access the
+ system's /etc/password, /etc/group files (and your system will be
+ smaller, and I will get fewer emails asking about how glibc NSS
+ works). When this option is enabled, you will not be able to use
+ PAM to access remote LDAP password servers and whatnot. And if you
+ want hostname resolution to work with glibc, you still need the
+ /lib/libnss_* libraries.
+
+ If you need to use glibc's nsswitch.conf mechanism
+ (e.g. if user/group database is NOT stored in /etc/passwd etc),
+ you must NOT use this option.
+
+ If you enable this option, it will add about 1.5k.
+
+config USE_BB_SHADOW
+ bool "Use internal shadow password functions"
+ default y
+ depends on USE_BB_PWD_GRP && FEATURE_SHADOWPASSWDS
+ help
+ If you leave this disabled, busybox will use the system's shadow
+ password handling functions. And if you are using the GNU C library
+ (glibc), you will then need to install the /etc/nsswitch.conf
+ configuration file and the required /lib/libnss_* libraries in
+ order for the shadow password functions to work. This generally
+ makes your embedded system quite a bit larger.
+
+ Enabling this option will cause busybox to directly access the
+ system's /etc/shadow file when handling shadow passwords. This
+ makes your system smaller (and I will get fewer emails asking about
+ how glibc NSS works). When this option is enabled, you will not be
+ able to use PAM to access shadow passwords from remote LDAP
+ password servers and whatnot.
+
+config USE_BB_CRYPT
+ bool "Use internal crypt functions"
+ default y
+ help
+ Busybox has internal DES and MD5 crypt functions.
+ They produce results which are identical to corresponding
+ standard C library functions.
+
+ If you leave this disabled, busybox will use the system's
+ crypt functions. Most C libraries use large (~70k)
+ static buffers there, and also combine them with more general
+ DES encryption/decryption.
- Enabling this option will cause busybox to directly access the
- system's /etc/password, /etc/group files (and your system will be
- smaller, and I will get fewer emails asking about how glibc NSS
- works). When this option is enabled, you will not be able to use
- PAM to access remote LDAP password servers and whatnot. And if you
- want hostname resolution to work with glibc, you still need the
- /lib/libnss_* libraries.
+ For busybox, having large static buffers is undesirable,
+ especially on NOMMU machines. Busybox also doesn't need
+ DES encryption/decryption and can do with smaller code.
- If you enable this option, it will add about 1.5k to busybox.
+ If you enable this option, it will add about 4.8k of code
+ if you are building dynamically linked executable.
+ In static build, it makes code _smaller_ by about 1.2k,
+ and likely many kilobytes less of bss.
+config USE_BB_CRYPT_SHA
+ bool "Enable SHA256/512 crypt functions"
+ default n
+ depends on USE_BB_CRYPT
+ help
+ Enable this if you have passwords starting with "$5$" or "$6$"
+ in your /etc/passwd or /etc/shadow files. These passwords
+ are hashed using SHA256 and SHA512 algorithms. Support for them
+ was added to glibc in 2008.
+ With this option off, login will fail password check for any
+ user which has password encrypted with these algorithms.
-config CONFIG_ADDGROUP
+config ADDGROUP
bool "addgroup"
default n
help
Utility for creating a new group account.
-config CONFIG_DELGROUP
+config FEATURE_ADDUSER_TO_GROUP
+ bool "Support for adding users to groups"
+ default n
+ depends on ADDGROUP
+ help
+ If called with two non-option arguments,
+ addgroup will add an existing user to an
+ existing group.
+
+config DELGROUP
bool "delgroup"
default n
help
Utility for deleting a group account.
-config CONFIG_ADDUSER
+config FEATURE_DEL_USER_FROM_GROUP
+ bool "Support for removing users from groups"
+ default n
+ depends on DELGROUP
+ help
+ If called with two non-option arguments, deluser
+ or delgroup will remove an user from a specified group.
+
+config FEATURE_CHECK_NAMES
+ bool "Enable sanity check on user/group names in adduser and addgroup"
+ default n
+ depends on ADDUSER || ADDGROUP
+ help
+ Enable sanity check on user and group names in adduser and addgroup.
+ To avoid problems, the user or group name should consist only of
+ letters, digits, underscores, periods, at signs and dashes,
+ and not start with a dash (as defined by IEEE Std 1003.1-2001).
+ For compatibility with Samba machine accounts "$" is also supported
+ at the end of the user or group name.
+
+config ADDUSER
bool "adduser"
default n
help
Utility for creating a new user account.
-config CONFIG_DELUSER
+config FEATURE_ADDUSER_LONG_OPTIONS
+ bool "Enable long options"
+ default n
+ depends on ADDUSER && GETOPT_LONG
+ help
+ Support long options for the adduser applet.
+
+config DELUSER
bool "deluser"
default n
help
Utility for deleting a user account.
-config CONFIG_GETTY
+config GETTY
bool "getty"
default n
+ select FEATURE_SYSLOG
+ help
+ getty lets you log in on a tty, it is normally invoked by init.
+
+config FEATURE_UTMP
+ bool "Support utmp file"
+ depends on GETTY || LOGIN || SU || WHO
+ default n
help
- Getty lets you log in on a tty, it is normally invoked by init.
+ The file /var/run/utmp is used to track who is currently logged in.
-config CONFIG_LOGIN
+config FEATURE_WTMP
+ bool "Support wtmp file"
+ depends on GETTY || LOGIN || SU || LAST
+ default n
+ select FEATURE_UTMP
+ help
+ The file /var/run/wtmp is used to track when user's have logged into
+ and logged out of the system.
+
+config LOGIN
bool "login"
default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
help
- Login is used when signing onto a system.
+ login is used when signing onto a system.
-config CONFIG_FEATURE_SECURETTY
- bool " Support for /etc/securetty"
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+config PAM
+ bool "Support for PAM (Pluggable Authentication Modules)"
+ default n
+ depends on LOGIN
+ help
+ Use PAM in login(1) instead of direct access to password database.
+
+config LOGIN_SCRIPTS
+ bool "Support for login scripts"
+ depends on LOGIN
+ default n
+ help
+ Enable this if you want login to execute $LOGIN_PRE_SUID_SCRIPT
+ just prior to switching from root to logged-in user.
+
+config FEATURE_NOLOGIN
+ bool "Support for /etc/nologin"
+ default y
+ depends on LOGIN
+ help
+ The file /etc/nologin is used by (some versions of) login(1).
+ If it exists, non-root logins are prohibited.
+
+config FEATURE_SECURETTY
+ bool "Support for /etc/securetty"
default y
- depends on CONFIG_LOGIN
+ depends on LOGIN
help
- The file /etc/securetty is used by (some versions of) login(1). The
- file contains the device names of tty lines (one per line, without
- leading /dev/) on which root is allowed to login.
+ The file /etc/securetty is used by (some versions of) login(1).
+ The file contains the device names of tty lines (one per line,
+ without leading /dev/) on which root is allowed to login.
-config CONFIG_PASSWD
+config PASSWD
bool "passwd"
default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
help
- Passwd changes passwords for user and group accounts. A normal user
- may only change the password for his/her own account, the super user
- may change the password for any account. The administrator of a group
+ passwd changes passwords for user and group accounts. A normal user
+ may only change the password for his/her own account, the super user
+ may change the password for any account. The administrator of a group
may change the password for the group.
-config CONFIG_SU
- bool "su"
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+config FEATURE_PASSWD_WEAK_CHECK
+ bool "Check new passwords for weakness"
+ default y
+ depends on PASSWD
+ help
+ With this option passwd will refuse new passwords which are "weak".
+
+config CRYPTPW
+ bool "cryptpw"
default n
help
- su is used to become another user during a login session. Invoked with-
- out a username, su defaults to becoming the super user.
+ Encrypts the given password with the crypt(3) libc function
+ using the given salt. Debian has this utility under mkpasswd
+ name. Busybox provides mkpasswd as an alias for cryptpw.
-config CONFIG_SULOGIN
- bool "sulogin"
+config CHPASSWD
+ bool "chpasswd"
default n
help
- Sulogin is invoked when the system goes into single user
- mode (this is done through an entry in inittab).
+ Reads a file of user name and password pairs from standard input
+ and uses this information to update a group of existing users.
-config CONFIG_VLOCK
- bool "vlock"
+config SU
+ bool "su"
default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
help
- Build the "vlock" applet, that allows you to lock (virtual) terminals.
+ su is used to become another user during a login session.
+ Invoked without a username, su defaults to becoming the super user.
-comment "Common options for adduser, deluser, login, su"
- depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
-config CONFIG_FEATURE_SHADOWPASSWDS
- bool "Support for shadow passwords"
+config FEATURE_SU_SYSLOG
+ bool "Enable su to write to syslog"
+ default y
+ depends on SU
+
+config FEATURE_SU_CHECKS_SHELLS
+ bool "Enable su to check user's shell to be listed in /etc/shells"
+ depends on SU
+ default y
+
+config SULOGIN
+ bool "sulogin"
default n
- depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU
+ select FEATURE_SYSLOG
help
- Build support for shadow password in /etc/shadow. This file is only
- readable by root and thus the encrypted passwords are no longer
- publicly readable.
+ sulogin is invoked when the system goes into single user
+ mode (this is done through an entry in inittab).
-config CONFIG_USE_BB_SHADOW
- bool " Use busybox shadow password functions"
+config VLOCK
+ bool "vlock"
default n
- depends on CONFIG_USE_BB_PWD_GRP && CONFIG_FEATURE_SHADOWPASSWDS
+ select FEATURE_SUID
help
- If you leave this disabled, busybox will use the system's shadow
- password handling functions. And if you are using the GNU C library
- (glibc), you will then need to install the /etc/nsswitch.conf
- configuration file and the required /lib/libnss_* libraries in
- order for the shadow password functions to work. This generally
- makes your embedded system quite a bit larger.
+ Build the "vlock" applet which allows you to lock (virtual) terminals.
- Enabling this option will cause busybox to directly access the
- system's /etc/shadow file when handling shadow passwords. This
- makes your system smaller and I will get fewer emails asking about
- how glibc NSS works). When this option is enabled, you will not be
- able to use PAM to access shadow passwords from remote LDAP
- password servers and whatnot.
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
endmenu
-
diff --git a/release/src/router/busybox/loginutils/Kbuild b/release/src/router/busybox/loginutils/Kbuild
new file mode 100644
index 00000000..3d0d777e
--- /dev/null
+++ b/release/src/router/busybox/loginutils/Kbuild
@@ -0,0 +1,19 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ADDGROUP) += addgroup.o
+lib-$(CONFIG_ADDUSER) += adduser.o
+lib-$(CONFIG_CRYPTPW) += cryptpw.o
+lib-$(CONFIG_CHPASSWD) += chpasswd.o
+lib-$(CONFIG_GETTY) += getty.o
+lib-$(CONFIG_LOGIN) += login.o
+lib-$(CONFIG_PASSWD) += passwd.o
+lib-$(CONFIG_SU) += su.o
+lib-$(CONFIG_SULOGIN) += sulogin.o
+lib-$(CONFIG_VLOCK) += vlock.o
+lib-$(CONFIG_DELUSER) += deluser.o
+lib-$(CONFIG_DELGROUP) += deluser.o
diff --git a/release/src/router/busybox/loginutils/Makefile b/release/src/router/busybox/loginutils/Makefile
deleted file mode 100644
index 3359d563..00000000
--- a/release/src/router/busybox/loginutils/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-TOPDIR:= ../
-LOGINUTILS_DIR:=./
-include $(TOPDIR).config
-include $(TOPDIR)Rules.mak
-include Makefile.in
-all: $(libraries-y)
--include $(TOPDIR).depend
-
-clean:
- rm -f *.o *.a $(AR_TARGET)
-
diff --git a/release/src/router/busybox/loginutils/Makefile.in b/release/src/router/busybox/loginutils/Makefile.in
deleted file mode 100755
index 31ce8bd9..00000000
--- a/release/src/router/busybox/loginutils/Makefile.in
+++ /dev/null
@@ -1,49 +0,0 @@
-# Makefile for busybox
-#
-# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org>
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation; either version 2 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
-#
-
-LOGINUTILS_AR:=loginutils.a
-ifndef LOGINUTILS_DIR
-LOGINUTILS_DIR:=$(TOPDIR)loginutils/
-endif
-
-LOGINUTILS-y:=
-LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o
-LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o
-LOGINUTILS-$(CONFIG_GETTY) += getty.o
-LOGINUTILS-$(CONFIG_LOGIN) += login.o
-LOGINUTILS-$(CONFIG_PASSWD) += passwd.o
-LOGINUTILS-$(CONFIG_SU) += su.o
-LOGINUTILS-$(CONFIG_SULOGIN) += sulogin.o
-LOGINUTILS-$(CONFIG_VLOCK) += vlock.o
-LOGINUTILS-$(CONFIG_DELUSER) += deluser.o
-LOGINUTILS-$(CONFIG_DELGROUP) += delgroup.o
-
-libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR)
-
-needcrypt-y:=
-needcrypt-$(CONFIG_LOGIN) := y
-needcrypt-$(CONFIG_SU) := y
-
-ifeq ($(needcrypt-y),y)
- LIBRARIES += -lcrypt
-endif
-
-$(LOGINUTILS_DIR)$(LOGINUTILS_AR): $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y))
- $(AR) -ro $@ $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y))
-
diff --git a/release/src/router/busybox/loginutils/addgroup.c b/release/src/router/busybox/loginutils/addgroup.c
index af1cd7a8..5a0cf3ff 100644
--- a/release/src/router/busybox/loginutils/addgroup.c
+++ b/release/src/router/busybox/loginutils/addgroup.c
@@ -1,171 +1,133 @@
/* vi: set sw=4 ts=4: */
/*
- * addgroup - add users to /etc/passwd and /etc/shadow
+ * addgroup - add groups to /etc/group and /etc/gshadow
*
* Copyright (C) 1999 by Lineo, inc. and John Beppu
* Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ * Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*
*/
+#include "libbb.h"
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include "busybox.h"
-#include "pwd_.h"
-#include "grp_.h"
-
-
-/* structs __________________________ */
-
-/* data _____________________________ */
-
-/* defaults : should this be in an external file? */
-static const char default_passwd[] = "x";
-
-
-/* make sure gr_name isn't taken, make sure gid is kosher
- * return 1 on failure */
-static int group_study(const char *filename, struct group *g)
+static void xgroup_study(struct group *g)
{
- FILE *etc_group;
- gid_t desired;
-
- struct group *grp;
- const int max = 65000;
-
- etc_group = bb_xfopen(filename, "r");
+ /* Make sure gr_name is unused */
+ if (getgrnam(g->gr_name)) {
+ goto error;
+ }
- /* make sure gr_name isn't taken, make sure gid is kosher */
- desired = g->gr_gid;
- while ((grp = fgetgrent(etc_group))) {
- if ((strcmp(grp->gr_name, g->gr_name)) == 0) {
- bb_error_msg_and_die("%s: group already in use\n", g->gr_name);
+ /* Check if the desired gid is free
+ * or find the first free one */
+ while (1) {
+ if (!getgrgid(g->gr_gid)) {
+ return; /* found free group: return */
}
- if ((desired) && grp->gr_gid == desired) {
- bb_error_msg_and_die("%d: gid has already been allocated\n",
- desired);
+ if (option_mask32) {
+ /* -g N, cannot pick gid other than N: error */
+ g->gr_name = itoa(g->gr_gid);
+ goto error;
}
- if ((grp->gr_gid > g->gr_gid) && (grp->gr_gid < max)) {
- g->gr_gid = grp->gr_gid;
+ g->gr_gid++;
+ if (g->gr_gid <= 0) {
+ /* overflowed: error */
+ bb_error_msg_and_die("no gids left");
}
}
- fclose(etc_group);
- /* gid */
- if (desired) {
- g->gr_gid = desired;
- } else {
- g->gr_gid++;
- }
- /* return 1; */
- return 0;
+ error:
+ /* exit */
+ bb_error_msg_and_die("group %s already exists", g->gr_name);
}
/* append a new user to the passwd file */
-static int addgroup(const char *filename, char *group, gid_t gid, const char *user)
+static void new_group(char *group, gid_t gid)
{
- FILE *etc_group;
-
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- FILE *etc_gshadow;
-#endif
-
struct group gr;
-
- /* group:passwd:gid:userlist */
- static const char entryfmt[] = "%s:%s:%d:%s\n";
+ char *p;
/* make sure gid and group haven't already been allocated */
gr.gr_gid = gid;
gr.gr_name = group;
- if (group_study(filename, &gr))
- return 1;
+ xgroup_study(&gr);
/* add entry to group */
- etc_group = bb_xfopen(filename, "a");
-
- fprintf(etc_group, entryfmt, group, default_passwd, gr.gr_gid, user);
- fclose(etc_group);
-
-
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- /* add entry to gshadow if necessary */
- if (access(bb_path_gshadow_file, F_OK|W_OK) == 0) {
- etc_gshadow = bb_xfopen(bb_path_gshadow_file, "a");
- fprintf(etc_gshadow, "%s:!::\n", group);
- fclose(etc_gshadow);
- }
+ p = xasprintf("x:%u:", gr.gr_gid);
+ if (update_passwd(bb_path_group_file, group, p, NULL) < 0)
+ exit(EXIT_FAILURE);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(p);
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* Ignore errors: if file is missing we suppose admin doesn't want it */
+ update_passwd(bb_path_gshadow_file, group, "!::", NULL);
#endif
-
- /* return 1; */
- return 0;
-}
-
-#ifndef CONFIG_ADDUSER
-static inline void if_i_am_not_root(void)
-{
- if (geteuid()) {
- bb_error_msg_and_die( "Only root may add a user or group to the system.");
- }
}
-#else
-extern void if_i_am_not_root(void);
-#endif
/*
* addgroup will take a login_name as its first parameter.
*
- * gid
- *
- * can be customized via command-line parameters.
- * ________________________________________________________________________ */
-int addgroup_main(int argc, char **argv)
+ * gid can be customized via command-line parameters.
+ * If called with two non-option arguments, addgroup
+ * will add an existing user to an existing group.
+ */
+int addgroup_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int addgroup_main(int argc UNUSED_PARAM, char **argv)
{
char *group;
- char *user;
gid_t gid = 0;
- /* get remaining args */
- if(bb_getopt_ulflags(argc, argv, "g:", &group)) {
- gid = strtol(group, NULL, 10);
+ /* need to be root */
+ if (geteuid()) {
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
}
- if (optind < argc) {
- group = argv[optind];
- optind++;
- } else {
- bb_show_usage();
+ /* Syntax:
+ * addgroup group
+ * addgroup -g num group
+ * addgroup user group
+ * Check for min, max and missing args */
+ opt_complementary = "-1:?2";
+ if (getopt32(argv, "g:", &group)) {
+ gid = xatoul_range(group, 0, ((unsigned long)(gid_t)ULONG_MAX) >> 1);
}
-
- if (optind < argc) {
- user = argv[optind];
- optind++;
- } else {
- user = "";
- }
- if_i_am_not_root();
+ /* move past the commandline options */
+ argv += optind;
+ //argc -= optind;
+
+#if ENABLE_FEATURE_ADDUSER_TO_GROUP
+ if (argv[1]) {
+ struct group *gr;
+
+ if (option_mask32) {
+ /* -g was there, but "addgroup -g num user group"
+ * is a no-no */
+ bb_show_usage();
+ }
- /* werk */
- return addgroup(bb_path_group_file, group, gid, user);
+ /* check if group and user exist */
+ xuname2uid(argv[0]); /* unknown user: exit */
+ gr = xgetgrnam(argv[1]); /* unknown group: exit */
+ /* check if user is already in this group */
+ for (; *(gr->gr_mem) != NULL; (gr->gr_mem)++) {
+ if (!strcmp(argv[0], *(gr->gr_mem))) {
+ /* user is already in group: do nothing */
+ return EXIT_SUCCESS;
+ }
+ }
+ if (update_passwd(bb_path_group_file, argv[1], NULL, argv[0]) < 0) {
+ return EXIT_FAILURE;
+ }
+# if ENABLE_FEATURE_SHADOWPASSWDS
+ update_passwd(bb_path_gshadow_file, argv[1], NULL, argv[0]);
+# endif
+ } else
+#endif /* ENABLE_FEATURE_ADDUSER_TO_GROUP */
+ {
+ die_if_bad_username(argv[0]);
+ new_group(argv[0], gid);
+
+ }
+ /* Reached only on success */
+ return EXIT_SUCCESS;
}
diff --git a/release/src/router/busybox/loginutils/adduser.c b/release/src/router/busybox/loginutils/adduser.c
index 41dc9f01..8a5d902e 100644
--- a/release/src/router/busybox/loginutils/adduser.c
+++ b/release/src/router/busybox/loginutils/adduser.c
@@ -5,316 +5,160 @@
* Copyright (C) 1999 by Lineo, inc. and John Beppu
* Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
*/
+#include "libbb.h"
-#ifndef _GNU_SOURCE
-#define _GNU_SOURCE
-#endif
-#include <errno.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <time.h>
-#include <unistd.h>
-#include <getopt.h>
-#include <sys/param.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include "busybox.h"
-
-
-
-/* structs __________________________ */
-
-typedef struct {
- uid_t u;
- gid_t g;
-} Id;
-
-/* data _____________________________ */
-
-/* defaults : should this be in an external file? */
-static const char default_passwd[] = "x";
-static const char default_gecos[] = "Linux User,,,";
-static const char default_home_prefix[] = "/home";
-static const char default_shell[] = "/bin/sh";
-
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
-/* shadow in use? */
-static int shadow_enabled = 0;
-#endif
+#define OPT_DONT_SET_PASS (1 << 4)
+#define OPT_SYSTEM_ACCOUNT (1 << 5)
+#define OPT_DONT_MAKE_HOME (1 << 6)
/* remix */
-/* EDR recoded such that the uid may be passed in *p */
-static int passwd_study(const char *filename, struct passwd *p)
+/* recoded such that the uid may be passed in *p */
+static void passwd_study(struct passwd *p)
{
- struct passwd *pw;
- FILE *passwd;
-
- const int min = 500;
- const int max = 65000;
+ int max;
- passwd = bb_wfopen(filename, "r");
- if (!passwd)
- return 4;
+ if (getpwnam(p->pw_name))
+ bb_error_msg_and_die("login '%s' is in use", p->pw_name);
- /* EDR if uid is out of bounds, set to min */
- if ((p->pw_uid > max) || (p->pw_uid < min))
- p->pw_uid = min;
-
- /* stuff to do:
- * make sure login isn't taken;
- * find free uid and gid;
- */
- while ((pw = fgetpwent(passwd))) {
- if (strcmp(pw->pw_name, p->pw_name) == 0) {
- /* return 0; */
- return 1;
- }
- if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
- && (pw->pw_uid >= min)) {
- p->pw_uid = pw->pw_uid + 1;
- }
+ if (option_mask32 & OPT_SYSTEM_ACCOUNT) {
+ p->pw_uid = 0;
+ max = 999;
+ } else {
+ p->pw_uid = 1000;
+ max = 64999;
}
- if (p->pw_gid == 0) {
- /* EDR check for an already existing gid */
- while (getgrgid(p->pw_uid) != NULL)
- p->pw_uid++;
-
- /* EDR also check for an existing group definition */
- if (getgrnam(p->pw_name) != NULL)
- return 3;
-
- /* EDR create new gid always = uid */
- p->pw_gid = p->pw_uid;
+ /* check for a free uid (and maybe gid) */
+ while (getpwuid(p->pw_uid) || (p->pw_gid == (gid_t)-1 && getgrgid(p->pw_uid))) {
+ p->pw_uid++;
+ if (p->pw_uid > max)
+ bb_error_msg_and_die("no free uids left");
}
- /* EDR bounds check */
- if ((p->pw_uid > max) || (p->pw_uid < min))
- return 2;
-
- /* return 1; */
- return 0;
+ if (p->pw_gid == (gid_t)-1) {
+ p->pw_gid = p->pw_uid; /* new gid = uid */
+ if (getgrnam(p->pw_name))
+ bb_error_msg_and_die("group name '%s' is in use", p->pw_name);
+ }
}
-static void addgroup_wrapper(const char *login, gid_t gid)
+static void addgroup_wrapper(struct passwd *p)
{
char *cmd;
- bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login);
+ cmd = xasprintf("addgroup -g %u '%s'", (unsigned)p->pw_gid, p->pw_name);
system(cmd);
free(cmd);
}
-static void passwd_wrapper(const char *login) __attribute__ ((noreturn));
+static void passwd_wrapper(const char *login) NORETURN;
static void passwd_wrapper(const char *login)
{
- static const char prog[] = "passwd";
- execlp(prog, prog, login, NULL);
- bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
-}
-
-/* putpwent(3) remix */
-static int adduser(const char *filename, struct passwd *p, int makehome, int setpass)
-{
- FILE *passwd;
- int r;
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- FILE *shadow;
- struct spwd *sp;
-#endif
- int new_group = 1;
-
- /* if using a pre-existing group, don't create one */
- if (p->pw_gid != 0)
- new_group = 0;
-
- /* make sure everything is kosher and setup uid && gid */
- passwd = bb_wfopen(filename, "a");
- if (passwd == NULL) {
- return 1;
- }
- fseek(passwd, 0, SEEK_END);
+ static const char prog[] ALIGN1 = "passwd";
- /* if (passwd_study(filename, p) == 0) { */
- r = passwd_study(filename, p);
- if (r) {
- if (r == 1)
- bb_error_msg("%s: login already in use", p->pw_name);
- else if (r == 2)
- bb_error_msg("illegal uid or no uids left");
- else if (r == 3)
- bb_error_msg("group name %s already in use", p->pw_name);
- else
- bb_error_msg("generic error.");
- return 1;
- }
-
- /* add to passwd */
- if (putpwent(p, passwd) == -1) {
- return 1;
- }
- fclose(passwd);
-
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- /* add to shadow if necessary */
- if (shadow_enabled) {
- shadow = bb_wfopen(bb_path_shadow_file, "a");
- if (shadow == NULL) {
- return 1;
- }
- fseek(shadow, 0, SEEK_END);
- sp = pwd_to_spwd(p);
- sp->sp_max = 99999; /* debianish */
- sp->sp_warn = 7;
- fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
- sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
- sp->sp_warn);
- fclose(shadow);
- }
-#endif
-
- if (new_group) {
- /* add to group */
- /* addgroup should be responsible for dealing w/ gshadow */
- addgroup_wrapper(p->pw_name, p->pw_gid);
- }
-
- /* Clear the umask for this process so it doesn't
- * * screw up the permissions on the mkdir and chown. */
- umask(0);
-
- if (makehome) {
- /* mkdir */
- if (mkdir(p->pw_dir, 0755)) {
- bb_perror_msg("%s", p->pw_dir);
- }
- /* Set the owner and group so it is owned by the new user. */
- if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
- bb_perror_msg("%s", p->pw_dir);
- }
- /* Now fix up the permissions to 2755. Can't do it before now
- * since chown will clear the setgid bit */
- if (chmod(p->pw_dir, 02755)) {
- bb_perror_msg("%s", p->pw_dir);
- }
- }
-
- if (setpass) {
- /* interactively set passwd */
- passwd_wrapper(p->pw_name);
- }
-
- return 0;
+ BB_EXECLP(prog, prog, login, NULL);
+ bb_error_msg_and_die("cannot execute %s, you must set password manually", prog);
}
-
-/* return current uid (root is always uid == 0, right?) */
-#ifndef CONFIG_ADDGROUP
-static inline void if_i_am_not_root(void)
-#else
-void if_i_am_not_root(void)
+#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
+static const char adduser_longopts[] ALIGN1 =
+ "home\0" Required_argument "h"
+ "gecos\0" Required_argument "g"
+ "shell\0" Required_argument "s"
+ "ingroup\0" Required_argument "G"
+ "disabled-password\0" No_argument "D"
+ "empty-password\0" No_argument "D"
+ "system\0" No_argument "S"
+ "no-create-home\0" No_argument "H"
+ ;
#endif
-{
- if (geteuid()) {
- bb_error_msg_and_die( "Only root may add a user or group to the system.");
- }
-}
-
-#define SETPASS 1
-#define MAKEHOME 4
/*
* adduser will take a login_name as its first parameter.
- *
- * home
- * shell
- * gecos
- *
+ * home, shell, gecos:
* can be customized via command-line parameters.
- * ________________________________________________________________________ */
-int adduser_main(int argc, char **argv)
+ */
+int adduser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int adduser_main(int argc UNUSED_PARAM, char **argv)
{
struct passwd pw;
- const char *login;
- const char *gecos = default_gecos;
- const char *home = NULL;
- const char *shell = default_shell;
- const char *usegroup = NULL;
- int flags;
- int setpass = 1;
- int makehome = 1;
+ const char *usegroup = NULL;
+ char *p;
- /* init */
- if (argc < 2) {
- bb_show_usage();
- }
- /* get args */
- flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &home, &gecos, &shell, &usegroup);
+#if ENABLE_FEATURE_ADDUSER_LONG_OPTIONS
+ applet_long_options = adduser_longopts;
+#endif
- if (flags & SETPASS) {
- setpass = 0;
- }
- if (flags & MAKEHOME) {
- makehome = 0;
+ /* got root? */
+ if (geteuid()) {
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
}
- /* got root? */
- if_i_am_not_root();
+ pw.pw_gecos = (char *)"Linux User,,,";
+ pw.pw_shell = (char *)DEFAULT_SHELL;
+ pw.pw_dir = NULL;
+
+ /* exactly one non-option arg */
+ opt_complementary = "=1";
+ getopt32(argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
+ argv += optind;
- /* get login */
- if (optind >= argc) {
- bb_error_msg_and_die( "no user specified");
+ /* fill in the passwd struct */
+ pw.pw_name = argv[0];
+ die_if_bad_username(pw.pw_name);
+ if (!pw.pw_dir) {
+ /* create string for $HOME if not specified already */
+ pw.pw_dir = xasprintf("/home/%s", argv[0]);
}
- login = argv[optind];
+ pw.pw_passwd = (char *)"x";
+ pw.pw_gid = usegroup ? xgroup2gid(usegroup) : -1; /* exits on failure */
- /* create string for $HOME if not specified already */
- if (!home) {
- home = concat_path_file(default_home_prefix, login);
+ /* make sure everything is kosher and setup uid && maybe gid */
+ passwd_study(&pw);
+
+ p = xasprintf("x:%u:%u:%s:%s:%s", pw.pw_uid, pw.pw_gid, pw.pw_gecos, pw.pw_dir, pw.pw_shell);
+ if (update_passwd(bb_path_passwd_file, pw.pw_name, p, NULL) < 0) {
+ return EXIT_FAILURE;
}
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- /* is /etc/shadow in use? */
- shadow_enabled = (0 == access(bb_path_shadow_file, F_OK));
-#endif
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(p);
- /* create a passwd struct */
- pw.pw_name = (char *)login;
- pw.pw_passwd = (char *)default_passwd;
- pw.pw_uid = 0;
- pw.pw_gid = 0;
- pw.pw_gecos = (char *)gecos;
- pw.pw_dir = (char *)home;
- pw.pw_shell = (char *)shell;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ p = xasprintf("!:%u:0:99999:7:::", (unsigned)(time(NULL) / 86400)); /* sp->sp_lstchg */
+ /* ignore errors: if file is missing we suppose admin doesn't want it */
+ update_passwd(bb_path_shadow_file, pw.pw_name, p, NULL);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ free(p);
+#endif
- if (usegroup) {
- /* Add user to a group that already exists */
- struct group *g;
+ /* add to group */
+ /* addgroup should be responsible for dealing w/ gshadow */
+ /* if using a pre-existing group, don't create one */
+ if (!usegroup)
+ addgroup_wrapper(&pw);
- g = getgrnam(usegroup);
- if (g == NULL)
- bb_error_msg_and_die("group %s does not exist", usegroup);
+ /* clear the umask for this process so it doesn't
+ * screw up the permissions on the mkdir and chown. */
+ umask(0);
+ if (!(option_mask32 & OPT_DONT_MAKE_HOME)) {
+ /* Set the owner and group so it is owned by the new user,
+ then fix up the permissions to 2755. Can't do it before
+ since chown will clear the setgid bit */
+ if (mkdir(pw.pw_dir, 0755)
+ || chown(pw.pw_dir, pw.pw_uid, pw.pw_gid)
+ || chmod(pw.pw_dir, 02755) /* set setgid bit on homedir */
+ ) {
+ bb_simple_perror_msg(pw.pw_dir);
+ }
+ }
- pw.pw_gid = g->gr_gid;
+ if (!(option_mask32 & OPT_DONT_SET_PASS)) {
+ /* interactively set passwd */
+ passwd_wrapper(pw.pw_name);
}
- /* grand finale */
- return adduser(bb_path_passwd_file, &pw, makehome, setpass);
+ return 0;
}
diff --git a/release/src/router/busybox/loginutils/chpasswd.c b/release/src/router/busybox/loginutils/chpasswd.c
new file mode 100644
index 00000000..4bffbe83
--- /dev/null
+++ b/release/src/router/busybox/loginutils/chpasswd.c
@@ -0,0 +1,71 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * chpasswd.c
+ *
+ * Written for SLIND (from passwd.c) by Alexander Shishkin <virtuoso@slind.org>
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include "libbb.h"
+
+#if ENABLE_GETOPT_LONG
+static const char chpasswd_longopts[] ALIGN1 =
+ "encrypted\0" No_argument "e"
+ "md5\0" No_argument "m"
+ ;
+#endif
+
+#define OPT_ENC 1
+#define OPT_MD5 2
+
+int chpasswd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int chpasswd_main(int argc UNUSED_PARAM, char **argv)
+{
+ char *name, *pass;
+ char salt[sizeof("$N$XXXXXXXX")];
+ int opt, rc;
+ int rnd = rnd; /* we *want* it to be non-initialized! */
+
+ if (getuid())
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+
+ opt_complementary = "m--e:e--m";
+ USE_GETOPT_LONG(applet_long_options = chpasswd_longopts;)
+ opt = getopt32(argv, "em");
+
+ while ((name = xmalloc_fgetline(stdin)) != NULL) {
+ pass = strchr(name, ':');
+ if (!pass)
+ bb_error_msg_and_die("missing new password");
+ *pass++ = '\0';
+
+ xuname2uid(name); /* dies if there is no such user */
+
+ if (!(opt & OPT_ENC)) {
+ rnd = crypt_make_salt(salt, 1, rnd);
+ if (opt & OPT_MD5) {
+ strcpy(salt, "$1$");
+ rnd = crypt_make_salt(salt + 3, 4, rnd);
+ }
+ pass = pw_encrypt(pass, salt, 0);
+ }
+
+ /* This is rather complex: if user is not found in /etc/shadow,
+ * we try to find & change his passwd in /etc/passwd */
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ rc = update_passwd(bb_path_shadow_file, name, pass, NULL);
+ if (rc == 0) /* no lines updated, no errors detected */
+#endif
+ rc = update_passwd(bb_path_passwd_file, name, pass, NULL);
+ /* LOGMODE_BOTH logs to syslog also */
+ logmode = LOGMODE_BOTH;
+ if (rc < 0)
+ bb_error_msg_and_die("an error occurred updating password for %s", name);
+ if (rc)
+ bb_info_msg("Password for '%s' changed", name);
+ logmode = LOGMODE_STDIO;
+ free(name);
+ if (!(opt & OPT_ENC))
+ free(pass);
+ }
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/loginutils/cryptpw.c b/release/src/router/busybox/loginutils/cryptpw.c
new file mode 100644
index 00000000..47212e18
--- /dev/null
+++ b/release/src/router/busybox/loginutils/cryptpw.c
@@ -0,0 +1,117 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * cryptpw.c - output a crypt(3)ed password to stdout.
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ * Cooked from passwd.c by Thomas Lundquist <thomasez@zelow.no>
+ * mkpasswd compatible options added by Bernhard Reutner-Fischer
+ *
+ * Licensed under GPLv2, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
+
+/* Debian has 'mkpasswd' utility, manpage says:
+
+NAME
+ mkpasswd - Overfeatured front end to crypt(3)
+SYNOPSIS
+ mkpasswd PASSWORD SALT
+...
+OPTIONS
+-S, --salt=STRING
+ Use the STRING as salt. It must not contain prefixes such as
+ $1$.
+-R, --rounds=NUMBER
+ Use NUMBER rounds. This argument is ignored if the method
+ choosen does not support variable rounds. For the OpenBSD Blowfish
+ method this is the logarithm of the number of rounds.
+-m, --method=TYPE
+ Compute the password using the TYPE method. If TYPE is 'help'
+ then the available methods are printed.
+-P, --password-fd=NUM
+ Read the password from file descriptor NUM instead of using getpass(3).
+ If the file descriptor is not connected to a tty then
+ no other message than the hashed password is printed on stdout.
+-s, --stdin
+ Like --password-fd=0.
+ENVIRONMENT
+ $MKPASSWD_OPTIONS
+ A list of options which will be evaluated before the ones
+ specified on the command line.
+BUGS
+ This programs suffers of a bad case of featuritis.
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+Very true...
+
+cryptpw was in bbox before this gem, so we retain it, and alias mkpasswd
+to cryptpw. -a option (alias for -m) came from cryptpw.
+*/
+
+int cryptpw_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int cryptpw_main(int argc UNUSED_PARAM, char **argv)
+{
+ /* $N$ + sha_salt_16_bytes + NUL */
+ char salt[3 + 16 + 1];
+ char *salt_ptr;
+ const char *opt_m, *opt_S;
+ int len;
+ int fd;
+
+#if ENABLE_GETOPT_LONG
+ static const char mkpasswd_longopts[] ALIGN1 =
+ "stdin\0" No_argument "s"
+ "password-fd\0" Required_argument "P"
+ "salt\0" Required_argument "S"
+ "method\0" Required_argument "m"
+ ;
+ applet_long_options = mkpasswd_longopts;
+#endif
+ fd = STDIN_FILENO;
+ opt_m = "d";
+ opt_S = NULL;
+ /* at most two non-option arguments; -P NUM */
+ opt_complementary = "?2:P+";
+ getopt32(argv, "sP:S:m:a:", &fd, &opt_S, &opt_m, &opt_m);
+ argv += optind;
+
+ /* have no idea how to handle -s... */
+
+ if (argv[0] && !opt_S)
+ opt_S = argv[1];
+
+ len = 2/2;
+ salt_ptr = salt;
+ if (opt_m[0] != 'd') { /* not des */
+ len = 8/2; /* so far assuming md5 */
+ *salt_ptr++ = '$';
+ *salt_ptr++ = '1';
+ *salt_ptr++ = '$';
+#if !ENABLE_USE_BB_CRYPT || ENABLE_USE_BB_CRYPT_SHA
+ if (opt_m[0] == 's') { /* sha */
+ salt[1] = '5' + (strcmp(opt_m, "sha512") == 0);
+ len = 16/2;
+ }
+#endif
+ }
+ if (opt_S)
+ safe_strncpy(salt_ptr, opt_S, sizeof(salt) - 3);
+ else
+ crypt_make_salt(salt_ptr, len, 0);
+
+ xmove_fd(fd, STDIN_FILENO);
+
+ puts(pw_encrypt(
+ argv[0] ? argv[0] : (
+ /* Only mkpasswd, and only from tty, prompts.
+ * Otherwise it is a plain read. */
+ (isatty(STDIN_FILENO) && applet_name[0] == 'm')
+ ? bb_ask_stdin("Password: ")
+ : xmalloc_fgetline(stdin)
+ ),
+ salt, 1));
+
+ return EXIT_SUCCESS;
+}
diff --git a/release/src/router/busybox/loginutils/delgroup.c b/release/src/router/busybox/loginutils/delgroup.c
deleted file mode 100644
index fe3dcb8d..00000000
--- a/release/src/router/busybox/loginutils/delgroup.c
+++ /dev/null
@@ -1,62 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * deluser (remove lusers from the system ;) for TinyLogin
- *
- * Copyright (C) 1999 by Lineo, inc. and John Beppu
- * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "busybox.h"
-
-
-#if ! defined CONFIG_DELUSER
-#include "delline.c"
-#else
-extern int del_line_matching(const char *login, const char *filename);
-#endif
-
-int delgroup_main(int argc, char **argv)
-{
- /* int successful; */
- int failure;
-
- if (argc != 2) {
- bb_show_usage();
- } else {
-
- failure = del_line_matching(argv[1], bb_path_group_file);
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- if (access(bb_path_gshadow_file, W_OK) == 0) {
- /* EDR the |= works if the error is not 0, so he had it wrong */
- failure |= del_line_matching(argv[1], bb_path_gshadow_file);
- }
-#endif
- if (failure) {
- bb_error_msg_and_die("%s: Group could not be removed\n", argv[1]);
- }
-
- }
- return (EXIT_SUCCESS);
-}
-
-/* $Id: delgroup.c,v 1.1.3.1 2004/12/29 07:07:45 honor Exp $ */
diff --git a/release/src/router/busybox/loginutils/delline.c b/release/src/router/busybox/loginutils/delline.c
deleted file mode 100644
index 3f5f73ff..00000000
--- a/release/src/router/busybox/loginutils/delline.c
+++ /dev/null
@@ -1,113 +0,0 @@
-/* vi: set sw=4 ts=4: */
-/*
- * deluser (remove lusers from the system ;) for TinyLogin
- *
- * Copyright (C) 1999 by Lineo, inc. and John Beppu
- * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
- */
-
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "busybox.h"
-
-
-
-/* where to start and stop deletion */
-typedef struct {
- size_t start;
- size_t stop;
-} Bounds;
-
-/* An interesting side-effect of boundary()'s
- * implementation is that the first user (typically root)
- * cannot be removed. Let's call it a feature. */
-static inline Bounds boundary(const char *buffer, const char *login)
-{
- char needle[256];
- char *start;
- char *stop;
- Bounds b;
-
- snprintf(needle, 256, "\n%s:", login);
- needle[255] = 0;
- start = strstr(buffer, needle);
- if (!start) {
- b.start = 0;
- b.stop = 0;
- return b;
- }
- start++;
-
- stop = index(start, '\n'); /* index is a BSD-ism */
- b.start = start - buffer;
- b.stop = stop - buffer;
- return b;
-}
-
-/* grep -v ^login (except it only deletes the first match) */
-/* ...in fact, I think I'm going to simplify this later */
-int del_line_matching(const char *login, const char *filename)
-{
- char *buffer;
- FILE *passwd;
- size_t len;
- Bounds b;
- struct stat statbuf;
-
- /* load into buffer */
- passwd = fopen(filename, "r");
- if (!passwd) {
- return 1;
- }
- stat(filename, &statbuf);
- len = statbuf.st_size;
- buffer = (char *) malloc(len * sizeof(char));
-
- if (!buffer) {
- fclose(passwd);
- return 1;
- }
- fread(buffer, len, sizeof(char), passwd);
-
- fclose(passwd);
-
- /* find the user to remove */
- b = boundary(buffer, login);
- if (b.stop == 0) {
- free(buffer);
- return 1;
- }
-
- /* write the file w/o the user */
- passwd = fopen(filename, "w");
- if (!passwd) {
- return 1;
- }
- fwrite(buffer, (b.start - 1), sizeof(char), passwd);
- fwrite(&buffer[b.stop], (len - b.stop), sizeof(char), passwd);
-
- fclose(passwd);
-
- return 0;
-}
-
-
-/* $Id: delline.c,v 1.1.3.1 2004/12/29 07:07:45 honor Exp $ */
diff --git a/release/src/router/busybox/loginutils/deluser.c b/release/src/router/busybox/loginutils/deluser.c
index f84076da..293e324b 100644
--- a/release/src/router/busybox/loginutils/deluser.c
+++ b/release/src/router/busybox/loginutils/deluser.c
@@ -1,68 +1,56 @@
/* vi: set sw=4 ts=4: */
/*
- * deluser (remove lusers from the system ;) for TinyLogin
+ * deluser/delgroup implementation for busybox
*
* Copyright (C) 1999 by Lineo, inc. and John Beppu
* Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
+ * Copyright (C) 2007 by Tito Ragusa <farmatito@tiscali.it>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
*
*/
+#include "libbb.h"
-#include <sys/stat.h>
-#include <unistd.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include "busybox.h"
-
-
-#include "delline.c"
-
-static const char deluser_format[]="%s: User could not be removed from %s";
+static int del_line_matching(char **args, const char *filename)
+{
+ if (ENABLE_FEATURE_DEL_USER_FROM_GROUP && args[2]) {
+ return update_passwd(filename, args[2], NULL, args[1]);
+ }
+ return update_passwd(filename, args[1], NULL, NULL);
+}
+int deluser_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
int deluser_main(int argc, char **argv)
{
- /* int successful; */
- int failure;
-
- if (argc != 2) {
+ if (argc != 2
+ && (!ENABLE_FEATURE_DEL_USER_FROM_GROUP
+ || (applet_name[3] != 'g' || argc != 3))
+ ) {
bb_show_usage();
- } else {
-
- failure = del_line_matching(argv[1], bb_path_passwd_file);
- if (failure) {
- bb_error_msg_and_die(deluser_format, argv[1], bb_path_passwd_file);
- }
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- failure = del_line_matching(argv[1], bb_path_shadow_file);
- if (failure) {
- bb_error_msg_and_die(deluser_format, argv[1], bb_path_shadow_file);
- }
- failure = del_line_matching(argv[1], bb_path_gshadow_file);
- if (failure) {
- bb_error_msg_and_die(deluser_format, argv[1], bb_path_gshadow_file);
- }
-#endif
- failure = del_line_matching(argv[1], bb_path_group_file);
- if (failure) {
- bb_error_msg_and_die(deluser_format, argv[1], bb_path_group_file);
- }
+ }
+ if (geteuid())
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+
+ if ((ENABLE_FEATURE_DEL_USER_FROM_GROUP && argc != 3)
+ || ENABLE_DELUSER
+ || (ENABLE_DELGROUP && ENABLE_DESKTOP)
+ ) {
+ if (ENABLE_DELUSER
+ && (!ENABLE_DELGROUP || applet_name[3] == 'u')
+ ) {
+ if (del_line_matching(argv, bb_path_passwd_file) < 0)
+ return EXIT_FAILURE;
+ if (ENABLE_FEATURE_SHADOWPASSWDS) {
+ del_line_matching(argv, bb_path_shadow_file);
+ }
+ } else if (ENABLE_DESKTOP && ENABLE_DELGROUP && getpwnam(argv[1]))
+ bb_error_msg_and_die("can't remove primary group of user %s", argv[1]);
+ }
+ if (del_line_matching(argv, bb_path_group_file) < 0)
+ return EXIT_FAILURE;
+ if (ENABLE_FEATURE_SHADOWPASSWDS) {
+ del_line_matching(argv, bb_path_gshadow_file);
}
- return (EXIT_SUCCESS);
+ return EXIT_SUCCESS;
}
-
-/* $Id: deluser.c,v 1.1.3.1 2004/12/29 07:07:45 honor Exp $ */
diff --git a/release/src/router/busybox/loginutils/getty.c b/release/src/router/busybox/loginutils/getty.c
index 4219ff82..24a182ff 100644
--- a/release/src/router/busybox/loginutils/getty.c
+++ b/release/src/router/busybox/loginutils/getty.c
@@ -1,697 +1,322 @@
/* vi: set sw=4 ts=4: */
/* agetty.c - another getty program for Linux. By W. Z. Venema 1989
- Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
- This program is freely distributable. The entire man-page used to
- be here. Now read the real man-page agetty.8 instead.
-
- -f option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
-
- 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
- - added Native Language Support
-
- 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
- - enable hardware flow control before displaying /etc/issue
-
-*/
-
-#include <stdio.h>
-#include <stdlib.h>
-#include <unistd.h>
-#include <string.h>
-#include <sys/ioctl.h>
-#include <errno.h>
-#include <sys/stat.h>
-#include <sys/signal.h>
-#include <fcntl.h>
-#include <stdarg.h>
-#include <ctype.h>
-#include <utmp.h>
-#include <getopt.h>
-#include <termios.h>
-#include "busybox.h"
-
-#define _PATH_LOGIN "/bin/login"
-
- /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
-#ifdef CONFIG_SYSLOGD
-#include <sys/param.h>
-#define USE_SYSLOG
-#include <syslog.h>
-#endif
-
+ * Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
+ * This program is freely distributable. The entire man-page used to
+ * be here. Now read the real man-page agetty.8 instead.
+ *
+ * option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
+ *
+ * 1999-02-22 Arkadiusz Mickiewicz <misiek@misiek.eu.org>
+ * - added Native Language Support
+ *
+ * 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
+ * - enable hardware flow control before displaying /etc/issue
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
- /*
- * Some heuristics to find out what environment we are in: if it is not
- * System V, assume it is SunOS 4.
- */
+#include "libbb.h"
+#include <syslog.h>
-#ifdef LOGIN_PROCESS /* defined in System V utmp.h */
-#define SYSV_STYLE /* select System V style getty */
-#ifdef CONFIG_FEATURE_U_W_TMP
-extern void updwtmp(const char *filename, const struct utmp *ut);
+#if ENABLE_FEATURE_UTMP
+#include <utmp.h> /* updwtmp() */
#endif
-#endif /* LOGIN_PROCESS */
- /*
- * Things you may want to modify.
- *
- * If ISSUE is not defined, agetty will never display the contents of the
- * /etc/issue file. You will not want to spit out large "issue" files at the
- * wrong baud rate. Relevant for System V only.
- *
- * You may disagree with the default line-editing etc. characters defined
- * below. Note, however, that DEL cannot be used for interrupt generation
- * and for line editing at the same time.
- */
-
-#ifdef SYSV_STYLE
-#define ISSUE "/etc/issue" /* displayed before the login prompt */
+/*
+ * Some heuristics to find out what environment we are in: if it is not
+ * System V, assume it is SunOS 4.
+ */
+#ifdef LOGIN_PROCESS /* defined in System V utmp.h */
#include <sys/utsname.h>
-#include <time.h>
-#endif
+#else /* if !sysV style, wtmp/utmp code is off */
+#undef ENABLE_FEATURE_UTMP
+#undef ENABLE_FEATURE_WTMP
+#define ENABLE_FEATURE_UTMP 0
+#define ENABLE_FEATURE_WTMP 0
+#endif /* LOGIN_PROCESS */
-/* Some shorthands for control characters. */
+/*
+ * Things you may want to modify.
+ *
+ * You may disagree with the default line-editing etc. characters defined
+ * below. Note, however, that DEL cannot be used for interrupt generation
+ * and for line editing at the same time.
+ */
-#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
-#define CR CTL('M') /* carriage return */
-#define NL CTL('J') /* line feed */
-#define BS CTL('H') /* back space */
-#define DEL CTL('?') /* delete */
+/* I doubt there are systems which still need this */
+#undef HANDLE_ALLCAPS
+#undef ANCIENT_BS_KILL_CHARS
-/* Defaults for line-editing etc. characters; you may want to change this. */
+#define _PATH_LOGIN "/bin/login"
-#define DEF_ERASE DEL /* default erase character */
-#define DEF_INTR CTL('C') /* default interrupt character */
-#define DEF_QUIT CTL('\\') /* default quit char */
-#define DEF_KILL CTL('U') /* default kill char */
-#define DEF_EOF CTL('D') /* default EOF char */
-#define DEF_EOL 0
-#define DEF_SWITCH 0 /* default switch char */
-
- /*
- * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
- * because the termio -> termios translation does not clear the termios
- * CIBAUD bits. Therefore, the tty driver would sometimes report that input
- * baud rate != output baud rate. I did not notice that problem with SunOS
- * 4.1. We will use termios where available, and termio otherwise.
- */
-
-/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
- properly, but all is well if we use termios?! */
-
-#ifdef TCGETS
-#undef TCGETA
-#undef TCSETA
-#undef TCSETAW
-#define termio termios
-#define TCGETA TCGETS
-#define TCSETA TCSETS
-#define TCSETAW TCSETSW
-#endif
+/* If ISSUE is not defined, getty will never display the contents of the
+ * /etc/issue file. You will not want to spit out large "issue" files at the
+ * wrong baud rate.
+ */
+#define ISSUE "/etc/issue" /* displayed before the login prompt */
- /*
- * This program tries to not use the standard-i/o library. This keeps the
- * executable small on systems that do not have shared libraries (System V
- * Release <3).
- */
-#ifndef BUFSIZ
-#define BUFSIZ 1024
-#endif
+/* Some shorthands for control characters. */
+#define CTL(x) ((x) ^ 0100) /* Assumes ASCII dialect */
+#define CR CTL('M') /* carriage return */
+#define NL CTL('J') /* line feed */
+#define BS CTL('H') /* back space */
+#define DEL CTL('?') /* delete */
- /*
- * When multiple baud rates are specified on the command line, the first one
- * we will try is the first one specified.
- */
+/* Defaults for line-editing etc. characters; you may want to change this. */
+#define DEF_ERASE DEL /* default erase character */
+#define DEF_INTR CTL('C') /* default interrupt character */
+#define DEF_QUIT CTL('\\') /* default quit char */
+#define DEF_KILL CTL('U') /* default kill char */
+#define DEF_EOF CTL('D') /* default EOF char */
+#define DEF_EOL '\n'
+#define DEF_SWITCH 0 /* default switch char */
-#define FIRST_SPEED 0
+/*
+ * When multiple baud rates are specified on the command line, the first one
+ * we will try is the first one specified.
+ */
+#define MAX_SPEED 10 /* max. nr. of baud rates */
/* Storage for command-line options. */
-
-#define MAX_SPEED 10 /* max. nr. of baud rates */
-
struct options {
- int flags; /* toggle switches, see below */
- int timeout; /* time-out period */
- char *login; /* login program */
- char *tty; /* name of tty */
- char *initstring; /* modem init string */
- char *issue; /* alternative issue file */
- int numspeed; /* number of baud rates to try */
- int speeds[MAX_SPEED]; /* baud rates to be tried */
+ int flags; /* toggle switches, see below */
+ unsigned timeout; /* time-out period */
+ const char *login; /* login program */
+ const char *tty; /* name of tty */
+ const char *initstring; /* modem init string */
+ const char *issue; /* alternative issue file */
+ int numspeed; /* number of baud rates to try */
+ int speeds[MAX_SPEED]; /* baud rates to be tried */
};
-#define F_PARSE (1<<0) /* process modem status messages */
-#define F_ISSUE (1<<1) /* display /etc/issue */
-#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
-#define F_LOCAL (1<<3) /* force local */
-#define F_INITSTRING (1<<4) /* initstring is set */
-#define F_WAITCRLF (1<<5) /* wait for CR or LF */
-#define F_CUSTISSUE (1<<6) /* give alternative issue file */
-#define F_NOPROMPT (1<<7) /* don't ask for login name! */
-
/* Storage for things detected while the login name was read. */
-
struct chardata {
- int erase; /* erase character */
- int kill; /* kill character */
- int eol; /* end-of-line character */
- int parity; /* what parity did we see */
- int capslock; /* upper case without lower case */
+ unsigned char erase; /* erase character */
+ unsigned char kill; /* kill character */
+ unsigned char eol; /* end-of-line character */
+ unsigned char parity; /* what parity did we see */
+ /* (parity & 1): saw odd parity char with 7th bit set */
+ /* (parity & 2): saw even parity char with 7th bit set */
+ /* parity == 0: probably 7-bit, space parity? */
+ /* parity == 1: probably 7-bit, odd parity? */
+ /* parity == 2: probably 7-bit, even parity? */
+ /* parity == 3: definitely 8 bit, no parity! */
+ /* Hmm... with any value of "parity" 8 bit, no parity is possible */
+#ifdef HANDLE_ALLCAPS
+ unsigned char capslock; /* upper case without lower case */
+#endif
};
-/* Initial values for the above. */
-struct chardata init_chardata = {
- DEF_ERASE, /* default erase character */
- DEF_KILL, /* default kill character */
- 13, /* default eol char */
- 0, /* space parity */
- 0, /* no capslock */
-};
-
-#if 0
-struct Speedtab {
- long speed;
- int code;
-};
-
-static struct Speedtab speedtab[] = {
- {50, B50},
- {75, B75},
- {110, B110},
- {134, B134},
- {150, B150},
- {200, B200},
- {300, B300},
- {600, B600},
- {1200, B1200},
- {1800, B1800},
- {2400, B2400},
- {4800, B4800},
- {9600, B9600},
-#ifdef B19200
- {19200, B19200},
-#endif
-#ifdef B38400
- {38400, B38400},
-#endif
-#ifdef EXTA
- {19200, EXTA},
-#endif
-#ifdef EXTB
- {38400, EXTB},
-#endif
-#ifdef B57600
- {57600, B57600},
-#endif
-#ifdef B115200
- {115200, B115200},
-#endif
-#ifdef B230400
- {230400, B230400},
+/* Initial values for the above. */
+static const struct chardata init_chardata = {
+ DEF_ERASE, /* default erase character */
+ DEF_KILL, /* default kill character */
+ 13, /* default eol char */
+ 0, /* space parity */
+#ifdef HANDLE_ALLCAPS
+ 0, /* no capslock */
#endif
- {0, 0},
};
-#endif
-static void parse_args(int argc, char **argv, struct options *op);
-static void parse_speeds(struct options *op, char *arg);
-static void open_tty(char *tty, struct termio *tp, int local);
-static void termio_init(struct termio *tp, int speed, struct options *op);
-static void auto_baud(struct termio *tp);
-static void do_prompt(struct options *op, struct termio *tp);
-static void next_speed(struct termio *tp, struct options *op);
-static char *get_logname(struct options *op, struct chardata *cp,
-
- struct termio *tp);
-static void termio_final(struct options *op, struct termio *tp,
-
- struct chardata *cp);
-static int caps_lock(const char *s);
-static int bcode(const char *s);
-static void error(const char *fmt, ...) __attribute__ ((noreturn));
-
-#ifdef CONFIG_FEATURE_U_W_TMP
-static void update_utmp(char *line);
-#endif
+static const char opt_string[] ALIGN1 = "I:LH:f:hil:mt:wn";
+#define F_INITSTRING (1 << 0) /* -I initstring is set */
+#define F_LOCAL (1 << 1) /* -L force local */
+#define F_FAKEHOST (1 << 2) /* -H fake hostname */
+#define F_CUSTISSUE (1 << 3) /* -f give alternative issue file */
+#define F_RTSCTS (1 << 4) /* -h enable RTS/CTS flow control */
+#define F_ISSUE (1 << 5) /* -i display /etc/issue */
+#define F_LOGIN (1 << 6) /* -l non-default login program */
+#define F_PARSE (1 << 7) /* -m process modem status messages */
+#define F_TIMEOUT (1 << 8) /* -t time out */
+#define F_WAITCRLF (1 << 9) /* -w wait for CR or LF */
+#define F_NOPROMPT (1 << 10) /* -n don't ask for login name */
-/* The following is used for understandable diagnostics. */
-/* Fake hostname for ut_host specified on command line. */
-static char *fakehost = NULL;
+#define line_buf bb_common_bufsiz1
-/* ... */
+/* The following is used for understandable diagnostics. */
#ifdef DEBUGGING
-#define debug(s) fprintf(dbf,s); fflush(dbf)
+static FILE *dbf;
#define DEBUGTERM "/dev/ttyp0"
-FILE *dbf;
+#define debug(...) do { fprintf(dbf, __VA_ARGS__); fflush(dbf); } while (0)
#else
-#define debug(s) /* nothing */
-#endif
-
-int getty_main(int argc, char **argv)
-{
- char *logname = NULL; /* login name, given to /bin/login */
- struct chardata chardata; /* set by get_logname() */
- struct termio termio; /* terminal mode bits */
- static struct options options = {
- F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
- 0, /* no timeout */
- _PATH_LOGIN, /* default login program */
- "tty1", /* default tty line */
- "", /* modem init string */
- ISSUE, /* default issue file */
- 0, /* no baud rates known yet */
- };
-
-#ifdef DEBUGGING
- dbf = bb_xfopen(DEBUGTERM, "w");
-
- {
- int i;
-
- for (i = 1; i < argc; i++) {
- debug(argv[i]);
- debug("\n");
- }
- }
-#endif
-
- /* Parse command-line arguments. */
-
- parse_args(argc, argv, &options);
-
-#ifdef __linux__
- setsid();
-#endif
-
- /* Update the utmp file. */
-
-
-#ifdef SYSV_STYLE
-#ifdef CONFIG_FEATURE_U_W_TMP
- update_utmp(options.tty);
-#endif
-#endif
-
- debug("calling open_tty\n");
- /* Open the tty as standard { input, output, error }. */
- open_tty(options.tty, &termio, options.flags & F_LOCAL);
-
-#ifdef __linux__
- {
- int iv;
-
- iv = getpid();
- ioctl(0, TIOCSPGRP, &iv);
- }
-#endif
- /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
- debug("calling termio_init\n");
- termio_init(&termio, options.speeds[FIRST_SPEED], &options);
-
- /* write the modem init string and DON'T flush the buffers */
- if (options.flags & F_INITSTRING) {
- debug("writing init string\n");
- write(1, options.initstring, strlen(options.initstring));
- }
-
- if (!(options.flags & F_LOCAL)) {
- /* go to blocking write mode unless -L is specified */
- fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
- }
-
- /* Optionally detect the baud rate from the modem status message. */
- debug("before autobaud\n");
- if (options.flags & F_PARSE)
- auto_baud(&termio);
-
- /* Set the optional timer. */
- if (options.timeout)
- (void) alarm((unsigned) options.timeout);
-
- /* optionally wait for CR or LF before writing /etc/issue */
- if (options.flags & F_WAITCRLF) {
- char ch;
-
- debug("waiting for cr-lf\n");
- while (read(0, &ch, 1) == 1) {
- ch &= 0x7f; /* strip "parity bit" */
-#ifdef DEBUGGING
- fprintf(dbf, "read %c\n", ch);
+#define debug(...) ((void)0)
#endif
- if (ch == '\n' || ch == '\r')
- break;
- }
- }
-
- chardata = init_chardata;
- if (!(options.flags & F_NOPROMPT)) {
- /* Read the login name. */
- debug("reading login name\n");
- /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */
- while ((logname = get_logname(&options, &chardata, &termio)) ==
- NULL) next_speed(&termio, &options);
- }
-
- /* Disable timer. */
- if (options.timeout)
- (void) alarm(0);
- /* Finalize the termio settings. */
-
- termio_final(&options, &termio, &chardata);
-
- /* Now the newline character should be properly written. */
-
- (void) write(1, "\n", 1);
-
- /* Let the login program take care of password validation. */
-
- (void) execl(options.login, options.login, "--", logname, (char *) 0);
- error("%s: can't exec %s: %m", options.tty, options.login);
-}
-
-/* parse-args - parse command-line arguments */
-
-static void parse_args(int argc, char **argv, struct options *op)
+/* bcode - convert speed string to speed code; return <= 0 on failure */
+static int bcode(const char *s)
{
- extern char *optarg; /* getopt */
- extern int optind; /* getopt */
- int c;
-
- while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) {
- switch (c) {
- case 'I':
- if (!(op->initstring = strdup(optarg)))
- error(bb_msg_memory_exhausted);
-
- {
- const char *p;
- char *q;
-
- /* copy optarg into op->initstring decoding \ddd
- octal codes into chars */
- q = op->initstring;
- p = optarg;
- while (*p) {
- if (*p == '\\') {
- p++;
- *q++ = bb_process_escape_sequence(&p);
- } else {
- *q++ = *p++;
- }
- }
- *q = '\0';
- }
- op->flags |= F_INITSTRING;
- break;
-
- case 'L': /* force local */
- op->flags |= F_LOCAL;
- break;
- case 'H': /* fake login host */
- fakehost = optarg;
- break;
- case 'f': /* custom issue file */
- op->flags |= F_CUSTISSUE;
- op->issue = optarg;
- break;
- case 'h': /* enable h/w flow control */
- op->flags |= F_RTSCTS;
- break;
- case 'i': /* do not show /etc/issue */
- op->flags &= ~F_ISSUE;
- break;
- case 'l':
- op->login = optarg; /* non-default login program */
- break;
- case 'm': /* parse modem status message */
- op->flags |= F_PARSE;
- break;
- case 'n':
- op->flags |= F_NOPROMPT;
- break;
- case 't': /* time out */
- if ((op->timeout = atoi(optarg)) <= 0)
- error("bad timeout value: %s", optarg);
- break;
- case 'w':
- op->flags |= F_WAITCRLF;
- break;
- default:
- bb_show_usage();
- }
- }
- debug("after getopt loop\n");
- if (argc < optind + 2) /* check parameter count */
- bb_show_usage();
-
- /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
- if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
- /* a number first, assume it's a speed (BSD style) */
- parse_speeds(op, argv[optind++]); /* baud rate(s) */
- op->tty = argv[optind]; /* tty name */
- } else {
- op->tty = argv[optind++]; /* tty name */
- parse_speeds(op, argv[optind]); /* baud rate(s) */
- }
-
- optind++;
- if (argc > optind && argv[optind])
- setenv("TERM", argv[optind], 1);
-
- debug("exiting parseargs\n");
+ int value = bb_strtou(s, NULL, 10); /* yes, int is intended! */
+ if (value < 0) /* bad terminating char, overflow, etc */
+ return value;
+ return tty_value_to_baud(value);
}
/* parse_speeds - parse alternate baud rates */
-
static void parse_speeds(struct options *op, char *arg)
{
char *cp;
+ /* NB: at least one iteration is always done */
debug("entered parse_speeds\n");
- for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
- if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
- error("bad speed: %s", cp);
+ while ((cp = strsep(&arg, ",")) != NULL) {
+ op->speeds[op->numspeed] = bcode(cp);
+ if (op->speeds[op->numspeed] < 0)
+ bb_error_msg_and_die("bad speed: %s", cp);
+ /* note: arg "0" turns into speed B0 */
+ op->numspeed++;
if (op->numspeed > MAX_SPEED)
- error("too many alternate speeds");
+ bb_error_msg_and_die("too many alternate speeds");
}
- debug("exiting parsespeeds\n");
+ debug("exiting parse_speeds\n");
}
-#ifdef SYSV_STYLE
-#ifdef CONFIG_FEATURE_U_W_TMP
-
-/* update_utmp - update our utmp entry */
-static void update_utmp(char *line)
+/* parse_args - parse command-line arguments */
+static void parse_args(char **argv, struct options *op, char **fakehost_p)
{
- struct utmp ut;
- struct utmp *utp;
- time_t t;
- int mypid = getpid();
-#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
- struct flock lock;
-#endif
-
- /*
- * The utmp file holds miscellaneous information about things started by
- * /sbin/init and other system-related events. Our purpose is to update
- * the utmp entry for the current process, in particular the process type
- * and the tty line we are listening to. Return successfully only if the
- * utmp file can be opened for update, and if we are able to find our
- * entry in the utmp file.
- */
- utmpname(_PATH_UTMP);
- setutent();
- while ((utp = getutent())
- && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */
- ;
+ char *ts;
+
+ opt_complementary = "-2:t+"; /* at least 2 args; -t N */
+ op->flags = getopt32(argv, opt_string,
+ &(op->initstring), fakehost_p, &(op->issue),
+ &(op->login), &op->timeout);
+ argv += optind;
+ if (op->flags & F_INITSTRING) {
+ const char *p = op->initstring;
+ char *q;
+
+ op->initstring = q = xstrdup(p);
+ /* copy optarg into op->initstring decoding \ddd
+ octal codes into chars */
+ while (*p) {
+ if (*p == '\\') {
+ p++;
+ *q++ = bb_process_escape_sequence(&p);
+ } else {
+ *q++ = *p++;
+ }
+ }
+ *q = '\0';
+ }
+ op->flags ^= F_ISSUE; /* invert flag "show /etc/issue" */
+ debug("after getopt\n");
- if (utp) {
- memcpy(&ut, utp, sizeof(ut));
- } else {
- /* some inits don't initialize utmp... */
- memset(&ut, 0, sizeof(ut));
- strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+ /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
+ op->tty = argv[0]; /* tty name */
+ ts = argv[1]; /* baud rate(s) */
+ if (isdigit(argv[0][0])) {
+ /* a number first, assume it's a speed (BSD style) */
+ op->tty = ts; /* tty name is in argv[1] */
+ ts = argv[0]; /* baud rate(s) */
}
- /*endutent(); */
+ parse_speeds(op, ts);
- strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
- strncpy(ut.ut_line, line, sizeof(ut.ut_line));
- if (fakehost)
- strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
- time(&t);
- ut.ut_time = t;
- ut.ut_type = LOGIN_PROCESS;
- ut.ut_pid = mypid;
+// TODO: if applet_name is set to "getty: TTY", bb_error_msg's get simpler!
+// grep for "%s:"
- pututline(&ut);
- endutent();
+ if (argv[2])
+ xsetenv("TERM", argv[2]);
- {
- updwtmp(_PATH_WTMP, &ut);
- }
+ debug("exiting parse_args\n");
}
-#endif /* CONFIG_FEATURE_U_W_TMP */
-#endif /* SYSV_STYLE */
-
/* open_tty - set up tty as standard { input, output, error } */
-static void open_tty(char *tty, struct termio *tp, int local)
+static void open_tty(const char *tty)
{
- /* Get rid of the present standard { output, error} if any. */
-
- (void) close(1);
- (void) close(2);
- errno = 0; /* ignore above errors */
-
/* Set up new standard input, unless we are given an already opened port. */
-
- if (strcmp(tty, "-")) {
- struct stat st;
+ if (NOT_LONE_DASH(tty)) {
+// struct stat st;
+// int cur_dir_fd;
+// int fd;
/* Sanity checks... */
+// cur_dir_fd = xopen(".", O_DIRECTORY | O_NONBLOCK);
+// xchdir("/dev");
+// xstat(tty, &st);
+// if ((st.st_mode & S_IFMT) != S_IFCHR)
+// bb_error_msg_and_die("%s: not a character device", tty);
- if (chdir("/dev"))
- error("/dev: chdir() failed: %m");
- if (stat(tty, &st) < 0)
- error("/dev/%s: %m", tty);
- if ((st.st_mode & S_IFMT) != S_IFCHR)
- error("/dev/%s: not a character device", tty);
+ if (tty[0] != '/')
+ tty = xasprintf("/dev/%s", tty); /* will leak it */
/* Open the tty as standard input. */
+ debug("open(2)\n");
+ close(0);
+ /*fd =*/ xopen(tty, O_RDWR | O_NONBLOCK); /* uses fd 0 */
- (void) close(0);
- errno = 0; /* ignore close(2) errors */
+// /* Restore current directory */
+// fchdir(cur_dir_fd);
- debug("open(2)\n");
- if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0)
- error("/dev/%s: cannot open as standard input: %m", tty);
+ /* Open the tty as standard input, continued */
+// xmove_fd(fd, 0);
+// /* fd is >= cur_dir_fd, and cur_dir_fd gets closed too here: */
+// while (fd > 2)
+// close(fd--);
+ /* Set proper protections and ownership. */
+ fchown(0, 0, 0); /* 0:0 */
+ fchmod(0, 0620); /* crw--w---- */
} else {
-
/*
* Standard input should already be connected to an open port. Make
* sure it is open for read/write.
*/
-
- if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
- error("%s: not open for read/write", tty);
+ if ((fcntl(0, F_GETFL) & O_RDWR) != O_RDWR)
+ bb_error_msg_and_die("stdin is not open for read/write");
}
-
- /* Set up standard output and standard error file descriptors. */
- debug("duping\n");
- if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
- error("%s: dup problem: %m", tty); /* we have a problem */
-
- /*
- * The following ioctl will fail if stdin is not a tty, but also when
- * there is noise on the modem control lines. In the latter case, the
- * common course of action is (1) fix your cables (2) give the modem more
- * time to properly reset after hanging up. SunOS users can achieve (2)
- * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
- * 5 seconds seems to be a good value.
- */
-
- if (ioctl(0, TCGETA, tp) < 0)
- error("%s: ioctl: %m", tty);
-
- /*
- * It seems to be a terminal. Set proper protections and ownership. Mode
- * 0622 is suitable for SYSV <4 because /bin/login does not change
- * protections. SunOS 4 login will change the protections to 0620 (write
- * access for group tty) after the login has succeeded.
- */
-
-#ifdef DEBIAN
- {
- /* tty to root.dialout 660 */
- struct group *gr;
- int id;
-
- id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0;
- chown(tty, 0, id);
- chmod(tty, 0660);
-
- /* vcs,vcsa to root.sys 600 */
- if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
- char *vcs, *vcsa;
-
- if (!(vcs = strdup(tty)))
- error("Can't malloc for vcs");
- if (!(vcsa = malloc(strlen(tty) + 2)))
- error("Can't malloc for vcsa");
- strcpy(vcs, "vcs");
- strcpy(vcs + 3, tty + 3);
- strcpy(vcsa, "vcsa");
- strcpy(vcsa + 4, tty + 3);
-
- id = (gr = getgrnam("sys")) ? gr->gr_gid : 0;
- chown(vcs, 0, id);
- chmod(vcs, 0600);
- chown(vcsa, 0, id);
- chmod(vcs, 0600);
-
- free(vcs);
- free(vcsa);
- }
- }
-#else
- (void) chown(tty, 0, 0); /* root, sys */
- (void) chmod(tty, 0622); /* crw--w--w- */
- errno = 0; /* ignore above errors */
-#endif
}
-/* termio_init - initialize termio settings */
-
-static void termio_init(struct termio *tp, int speed, struct options *op)
+/* termios_init - initialize termios settings */
+static void termios_init(struct termios *tp, int speed, struct options *op)
{
-
+ speed_t ispeed, ospeed;
/*
- * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
+ * Initial termios settings: 8-bit characters, raw-mode, blocking i/o.
* Special characters are set after we have read the login name; all
* reads will be done in raw mode anyway. Errors will be dealt with
- * lateron.
+ * later on.
*/
#ifdef __linux__
/* flush input and output queues, important for modems! */
- (void) ioctl(0, TCFLSH, TCIOFLUSH);
+ ioctl(0, TCFLSH, TCIOFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
#endif
-
- tp->c_cflag = CS8 | HUPCL | CREAD | speed;
- if (op->flags & F_LOCAL) {
- tp->c_cflag |= CLOCAL;
+ ispeed = ospeed = speed;
+ if (speed == B0) {
+ /* Speed was specified as "0" on command line.
+ * Just leave it unchanged */
+ ispeed = cfgetispeed(tp);
+ ospeed = cfgetospeed(tp);
}
+ tp->c_cflag = CS8 | HUPCL | CREAD;
+ if (op->flags & F_LOCAL)
+ tp->c_cflag |= CLOCAL;
+ cfsetispeed(tp, ispeed);
+ cfsetospeed(tp, ospeed);
- tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
+ tp->c_iflag = tp->c_lflag = tp->c_line = 0;
+ tp->c_oflag = OPOST | ONLCR;
tp->c_cc[VMIN] = 1;
tp->c_cc[VTIME] = 0;
/* Optionally enable hardware flow control */
-
-#ifdef CRTSCTS
+#ifdef CRTSCTS
if (op->flags & F_RTSCTS)
tp->c_cflag |= CRTSCTS;
#endif
- (void) ioctl(0, TCSETA, tp);
-
- /* go to blocking input even in local mode */
- fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
+ tcsetattr_stdin_TCSANOW(tp);
debug("term_io 2\n");
}
/* auto_baud - extract baud rate from modem status message */
-static void auto_baud(struct termio *tp)
+static void auto_baud(char *buf, unsigned size_buf, struct termios *tp)
{
int speed;
int vmin;
unsigned iflag;
- char buf[BUFSIZ];
char *bp;
int nread;
@@ -700,9 +325,9 @@ static void auto_baud(struct termio *tp)
* the DCD line, and if the computer is fast enough to set the proper
* baud rate before the message has gone by. We expect a message of the
* following format:
- *
+ *
* <junk><number><junk>
- *
+ *
* The number is interpreted as the baud rate of the incoming call. If the
* modem does not tell us the baud rate within one second, we will keep
* using the current baud rate. It is advisable to enable BREAK
@@ -712,26 +337,26 @@ static void auto_baud(struct termio *tp)
/*
* Use 7-bit characters, don't block if input queue is empty. Errors will
- * be dealt with lateron.
+ * be dealt with later on.
*/
-
iflag = tp->c_iflag;
- tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
+ tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
vmin = tp->c_cc[VMIN];
- tp->c_cc[VMIN] = 0; /* don't block if queue empty */
- (void) ioctl(0, TCSETA, tp);
+ tp->c_cc[VMIN] = 0; /* don't block if queue empty */
+ tcsetattr_stdin_TCSANOW(tp);
/*
* Wait for a while, then read everything the modem has said so far and
* try to extract the speed of the dial-in call.
*/
-
- (void) sleep(1);
- if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
+ sleep(1);
+ nread = safe_read(STDIN_FILENO, buf, size_buf - 1);
+ if (nread > 0) {
buf[nread] = '\0';
for (bp = buf; bp < buf + nread; bp++) {
- if (isascii(*bp) && isdigit(*bp)) {
- if ((speed = bcode(bp))) {
+ if (isdigit(*bp)) {
+ speed = bcode(bp);
+ if (speed > 0) {
tp->c_cflag &= ~CBAUD;
tp->c_cflag |= speed;
}
@@ -739,127 +364,132 @@ static void auto_baud(struct termio *tp)
}
}
}
- /* Restore terminal settings. Errors will be dealt with lateron. */
+ /* Restore terminal settings. Errors will be dealt with later on. */
tp->c_iflag = iflag;
tp->c_cc[VMIN] = vmin;
- (void) ioctl(0, TCSETA, tp);
+ tcsetattr_stdin_TCSANOW(tp);
}
/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
-static void do_prompt(struct options *op, struct termio *tp)
+static void do_prompt(struct options *op)
{
-#ifdef ISSUE /* optional: show /etc/issue */
+#ifdef ISSUE
print_login_issue(op->issue, op->tty);
#endif
print_login_prompt();
}
-/* next_speed - select next baud rate */
-static void next_speed(struct termio *tp, struct options *op)
+#ifdef HANDLE_ALLCAPS
+/* all_is_upcase - string contains upper case without lower case */
+/* returns 1 if true, 0 if false */
+static int all_is_upcase(const char *s)
{
- static int baud_index = FIRST_SPEED; /* current speed index */
-
- baud_index = (baud_index + 1) % op->numspeed;
- tp->c_cflag &= ~CBAUD;
- tp->c_cflag |= op->speeds[baud_index];
- (void) ioctl(0, TCSETA, tp);
+ while (*s)
+ if (islower(*s++))
+ return 0;
+ return 1;
}
+#endif
-/* get_logname - get user name, establish parity, speed, erase, kill, eol */
-/* return NULL on failure, logname on success */
-static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp)
+/* get_logname - get user name, establish parity, speed, erase, kill, eol;
+ * return NULL on BREAK, logname on success */
+static char *get_logname(char *logname, unsigned size_logname,
+ struct options *op, struct chardata *cp)
{
- static char logname[BUFSIZ];
char *bp;
- char c; /* input character, full eight bits */
- char ascval; /* low 7 bits of input character */
- int bits; /* # of "1" bits per character */
- int mask; /* mask with 1 bit up */
- static char *erase[] = { /* backspace-space-backspace */
- "\010\040\010", /* space parity */
- "\010\040\010", /* odd parity */
- "\210\240\210", /* even parity */
- "\210\240\210", /* no parity */
+ char c; /* input character, full eight bits */
+ char ascval; /* low 7 bits of input character */
+ int bits; /* # of "1" bits per character */
+ int mask; /* mask with 1 bit up */
+ static const char erase[][3] = {/* backspace-space-backspace */
+ "\010\040\010", /* space parity */
+ "\010\040\010", /* odd parity */
+ "\210\240\210", /* even parity */
+ "\010\040\010", /* 8 bit no parity */
};
- /* Initialize kill, erase, parity etc. (also after switching speeds). */
-
- *cp = init_chardata;
+ /* NB: *cp is pre-initialized with init_chardata */
/* Flush pending input (esp. after parsing or switching the baud rate). */
-
- (void) sleep(1);
- (void) ioctl(0, TCFLSH, TCIFLUSH);
+ sleep(1);
+ ioctl(0, TCFLSH, TCIFLUSH); /* tcflush(0, TCIOFLUSH)? - same */
/* Prompt for and read a login name. */
-
- for (*logname = 0; *logname == 0; /* void */ ) {
-
+ logname[0] = '\0';
+ while (!logname[0]) {
/* Write issue file and prompt, with "parity" bit == 0. */
-
- do_prompt(op, tp);
+ do_prompt(op);
/* Read name, watch for break, parity, erase, kill, end-of-line. */
-
- for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
+ bp = logname;
+ cp->eol = '\0';
+ while (cp->eol == '\0') {
/* Do not report trivial EINTR/EIO errors. */
-
- if (read(0, &c, 1) < 1) {
+ if (read(STDIN_FILENO, &c, 1) < 1) {
if (errno == EINTR || errno == EIO)
- exit(0);
- error("%s: read: %m", op->tty);
+ exit(EXIT_SUCCESS);
+ bb_perror_msg_and_die("%s: read", op->tty);
}
- /* Do BREAK handling elsewhere. */
- if ((c == 0) && op->numspeed > 1)
- /* return (0); */
+ /* BREAK. If we have speeds to try,
+ * return NULL (will switch speeds and return here) */
+ if (c == '\0' && op->numspeed > 1)
return NULL;
/* Do parity bit handling. */
-
- if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */
- for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
- if (mask & ascval)
- bits++; /* count "1" bits */
- cp->parity |= ((bits & 1) ? 1 : 2);
+ if (!(op->flags & F_LOCAL) && (c & 0x80)) { /* "parity" bit on? */
+ bits = 1;
+ mask = 1;
+ while (mask & 0x7f) {
+ if (mask & c)
+ bits++; /* count "1" bits */
+ mask <<= 1;
+ }
+ /* ... |= 2 - even, 1 - odd */
+ cp->parity |= 2 - (bits & 1);
}
- /* Do erase, kill and end-of-line processing. */
+ /* Do erase, kill and end-of-line processing. */
+ ascval = c & 0x7f;
switch (ascval) {
case CR:
case NL:
- *bp = 0; /* terminate logname */
- cp->eol = ascval; /* set end-of-line char */
+ *bp = '\0'; /* terminate logname */
+ cp->eol = ascval; /* set end-of-line char */
break;
case BS:
case DEL:
+#ifdef ANCIENT_BS_KILL_CHARS
case '#':
- cp->erase = ascval; /* set erase character */
+#endif
+ cp->erase = ascval; /* set erase character */
if (bp > logname) {
- (void) write(1, erase[cp->parity], 3);
+ full_write(STDOUT_FILENO, erase[cp->parity], 3);
bp--;
}
break;
case CTL('U'):
+#ifdef ANCIENT_BS_KILL_CHARS
case '@':
- cp->kill = ascval; /* set kill character */
+#endif
+ cp->kill = ascval; /* set kill character */
while (bp > logname) {
- (void) write(1, erase[cp->parity], 3);
+ full_write(STDOUT_FILENO, erase[cp->parity], 3);
bp--;
}
break;
case CTL('D'):
- exit(0);
+ exit(EXIT_SUCCESS);
default:
- if (!isascii(ascval) || !isprint(ascval)) {
- /* ignore garbage characters */ ;
- } else if (bp - logname >= sizeof(logname) - 1) {
- error("%s: input overrun", op->tty);
+ if (!isprint(ascval)) {
+ /* ignore garbage characters */
+ } else if ((int)(bp - logname) >= size_logname - 1) {
+ bb_error_msg_and_die("%s: input overrun", op->tty);
} else {
- (void) write(1, &c, 1); /* echo the character */
- *bp++ = ascval; /* and store it */
+ full_write(STDOUT_FILENO, &c, 1); /* echo the character */
+ *bp++ = ascval; /* and store it */
}
break;
}
@@ -867,153 +497,291 @@ static char *get_logname(struct options *op, struct chardata *cp, struct termio
}
/* Handle names with upper case and no lower case. */
- if ((cp->capslock = caps_lock(logname))) {
+#ifdef HANDLE_ALLCAPS
+ cp->capslock = all_is_upcase(logname);
+ if (cp->capslock) {
for (bp = logname; *bp; bp++)
if (isupper(*bp))
- *bp = tolower(*bp); /* map name to lower case */
+ *bp = tolower(*bp); /* map name to lower case */
}
- return (logname);
+#endif
+ return logname;
}
-/* termio_final - set the final tty mode bits */
-static void termio_final(struct options *op, struct termio *tp, struct chardata *cp)
+/* termios_final - set the final tty mode bits */
+static void termios_final(struct options *op, struct termios *tp, struct chardata *cp)
{
/* General terminal-independent stuff. */
-
- tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
+ tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
/* no longer| ECHOCTL | ECHOPRT */
tp->c_oflag |= OPOST;
/* tp->c_cflag = 0; */
- tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
- tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
- tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
+ tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
+ tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
+ tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
tp->c_cc[VEOL] = DEF_EOL;
- tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
+ tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
/* Account for special characters seen in input. */
-
if (cp->eol == CR) {
- tp->c_iflag |= ICRNL; /* map CR in input to NL */
- tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
+ tp->c_iflag |= ICRNL; /* map CR in input to NL */
+ tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
}
- tp->c_cc[VERASE] = cp->erase; /* set erase character */
- tp->c_cc[VKILL] = cp->kill; /* set kill character */
+ tp->c_cc[VERASE] = cp->erase; /* set erase character */
+ tp->c_cc[VKILL] = cp->kill; /* set kill character */
/* Account for the presence or absence of parity bits in input. */
-
switch (cp->parity) {
- case 0: /* space (always 0) parity */
+ case 0: /* space (always 0) parity */
+// I bet most people go here - they use only 7-bit chars in usernames....
break;
- case 1: /* odd parity */
+ case 1: /* odd parity */
tp->c_cflag |= PARODD;
/* FALLTHROUGH */
- case 2: /* even parity */
+ case 2: /* even parity */
tp->c_cflag |= PARENB;
tp->c_iflag |= INPCK | ISTRIP;
/* FALLTHROUGH */
- case (1 | 2): /* no parity bit */
+ case (1 | 2): /* no parity bit */
tp->c_cflag &= ~CSIZE;
tp->c_cflag |= CS7;
+// FIXME: wtf? case 3: we saw both even and odd 8-bit bytes -
+// it's probably some umlauts etc, but definitely NOT 7-bit!!!
+// Entire parity detection madness here just begs for deletion...
break;
}
- /* Account for upper case without lower case. */
+ /* Account for upper case without lower case. */
+#ifdef HANDLE_ALLCAPS
if (cp->capslock) {
tp->c_iflag |= IUCLC;
tp->c_lflag |= XCASE;
tp->c_oflag |= OLCUC;
}
+#endif
/* Optionally enable hardware flow control */
-
-#ifdef CRTSCTS
+#ifdef CRTSCTS
if (op->flags & F_RTSCTS)
tp->c_cflag |= CRTSCTS;
#endif
/* Finally, make the new settings effective */
+ /* It's tcsetattr_stdin_TCSANOW() + error check */
+ ioctl_or_perror_and_die(0, TCSETS, tp, "%s: TCSETS", op->tty);
+}
- if (ioctl(0, TCSETA, tp) < 0)
- error("%s: ioctl: TCSETA: %m", op->tty);
+#if ENABLE_FEATURE_UTMP
+static void touch(const char *filename)
+{
+ if (access(filename, R_OK | W_OK) == -1)
+ close(open(filename, O_WRONLY | O_CREAT, 0664));
}
-/* caps_lock - string contains upper case without lower case */
-/* returns 1 if true, 0 if false */
-static int caps_lock(const char *s)
+/* update_utmp - update our utmp entry */
+static void update_utmp(const char *line, char *fakehost)
{
- int capslock;
+ struct utmp ut;
+ struct utmp *utp;
+ int mypid = getpid();
+
+ /* In case we won't find an entry below... */
+ memset(&ut, 0, sizeof(ut));
+ safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+
+ /*
+ * The utmp file holds miscellaneous information about things started by
+ * /sbin/init and other system-related events. Our purpose is to update
+ * the utmp entry for the current process, in particular the process type
+ * and the tty line we are listening to. Return successfully only if the
+ * utmp file can be opened for update, and if we are able to find our
+ * entry in the utmp file.
+ */
+ touch(_PATH_UTMP);
- for (capslock = 0; *s; s++) {
- if (islower(*s))
- return (0);
- if (capslock == 0)
- capslock = isupper(*s);
+ utmpname(_PATH_UTMP);
+ setutent();
+ while ((utp = getutent()) != NULL) {
+ if (utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid) {
+ memcpy(&ut, utp, sizeof(ut));
+ break;
+ }
}
- return (capslock);
+
+ strcpy(ut.ut_user, "LOGIN");
+ safe_strncpy(ut.ut_line, line, sizeof(ut.ut_line));
+ if (fakehost)
+ safe_strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
+ ut.ut_tv.tv_sec = time(NULL);
+ ut.ut_type = LOGIN_PROCESS;
+ ut.ut_pid = mypid;
+
+ pututline(&ut);
+ endutent();
+
+#if ENABLE_FEATURE_WTMP
+ touch(bb_path_wtmp_file);
+ updwtmp(bb_path_wtmp_file, &ut);
+#endif
}
+#endif /* CONFIG_FEATURE_UTMP */
-/* bcode - convert speed string to speed code; return 0 on failure */
-static int bcode(const char *s)
+int getty_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int getty_main(int argc UNUSED_PARAM, char **argv)
{
-#if 0
- struct Speedtab *sp;
- long speed = atol(s);
-
- for (sp = speedtab; sp->speed; sp++)
- if (sp->speed == speed)
- return (sp->code);
- return (0);
-#else
- int r;
+ int n;
+ char *fakehost = NULL; /* Fake hostname for ut_host */
+ char *logname; /* login name, given to /bin/login */
+ /* Merging these into "struct local" may _seem_ to reduce
+ * parameter passing, but today's gcc will inline
+ * statics which are called once anyway, so don't do that */
+ struct chardata chardata; /* set by get_logname() */
+ struct termios termios; /* terminal mode bits */
+ struct options options;
- if ((r = bb_value_to_baud(atol(s))) > 0) {
- return r;
- }
- return 0;
+ chardata = init_chardata;
+
+ memset(&options, 0, sizeof(options));
+ options.login = _PATH_LOGIN; /* default login program */
+ options.tty = "tty1"; /* default tty line */
+ options.initstring = ""; /* modem init string */
+#ifdef ISSUE
+ options.issue = ISSUE; /* default issue file */
#endif
-}
-/* error - report errors to console or syslog; only understands %s and %m */
+ /* Parse command-line arguments. */
+ parse_args(argv, &options, &fakehost);
-#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
+ logmode = LOGMODE_NONE;
-/*
- * output error messages
- */
-static void error(const char *fmt, ...)
-{
- va_list va_alist;
- char buf[256], *bp;
+ /* Create new session, lose controlling tty, if any */
+ /* docs/ctty.htm says:
+ * "This is allowed only when the current process
+ * is not a process group leader" - is this a problem? */
+ setsid();
+ /* close stdio, and stray descriptors, just in case */
+ n = xopen(bb_dev_null, O_RDWR);
+ /* dup2(n, 0); - no, we need to handle "getty - 9600" too */
+ xdup2(n, 1);
+ xdup2(n, 2);
+ while (n > 2)
+ close(n--);
+
+ /* Logging. We want special flavor of error_msg_and_die */
+ die_sleep = 10;
+ msg_eol = "\r\n";
+ /* most likely will internally use fd #3 in CLOEXEC mode: */
+ openlog(applet_name, LOG_PID, LOG_AUTH);
+ logmode = LOGMODE_BOTH;
-#ifndef USE_SYSLOG
- int fd;
+#ifdef DEBUGGING
+ dbf = xfopen_for_write(DEBUGTERM);
+ for (n = 1; argv[n]; n++) {
+ debug(argv[n]);
+ debug("\n");
+ }
#endif
-#ifdef USE_SYSLOG
- buf[0] = '\0';
- bp = buf;
-#else
- strncpy(buf, bb_applet_name, 256);
- strncat(buf, ": ", 256);
- buf[255] = 0;
- bp = buf + strlen(buf);
+ /* Open the tty as standard input, if it is not "-" */
+ /* If it's not "-" and not taken yet, it will become our ctty */
+ debug("calling open_tty\n");
+ open_tty(options.tty);
+ ndelay_off(0);
+ debug("duping\n");
+ xdup2(0, 1);
+ xdup2(0, 2);
+
+ /*
+ * The following ioctl will fail if stdin is not a tty, but also when
+ * there is noise on the modem control lines. In the latter case, the
+ * common course of action is (1) fix your cables (2) give the modem more
+ * time to properly reset after hanging up. SunOS users can achieve (2)
+ * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
+ * 5 seconds seems to be a good value.
+ */
+ /* tcgetattr() + error check */
+ ioctl_or_perror_and_die(0, TCGETS, &termios, "%s: TCGETS", options.tty);
+
+#ifdef __linux__
+// FIXME: do we need this? Otherwise "-" case seems to be broken...
+ // /* Forcibly make fd 0 our controlling tty, even if another session
+ // * has it as a ctty. (Another session loses ctty). */
+ // ioctl(0, TIOCSCTTY, (void*)1);
+ /* Make ourself a foreground process group within our session */
+ tcsetpgrp(0, getpid());
#endif
- va_start(va_alist, fmt);
- vsnprintf(bp, 256 - strlen(buf), fmt, va_alist);
- buf[255] = 0;
- va_end(va_alist);
+#if ENABLE_FEATURE_UTMP
+ /* Update the utmp file. This tty is ours now! */
+ update_utmp(options.tty, fakehost);
+#endif
-#ifdef USE_SYSLOG
- syslog_msg(LOG_AUTH, LOG_ERR, buf);
-#else
- strncat(bp, "\r\n", 256 - strlen(buf));
- buf[255] = 0;
- if ((fd = open("/dev/console", 1)) >= 0) {
- write(fd, buf, strlen(buf));
- close(fd);
+ /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
+ debug("calling termios_init\n");
+ termios_init(&termios, options.speeds[0], &options);
+
+ /* Write the modem init string and DON'T flush the buffers */
+ if (options.flags & F_INITSTRING) {
+ debug("writing init string\n");
+ /* todo: use xwrite_str? */
+ full_write(STDOUT_FILENO, options.initstring, strlen(options.initstring));
}
-#endif
- (void) sleep((unsigned) 10); /* be kind to init(8) */
- exit(1);
+
+ /* Optionally detect the baud rate from the modem status message */
+ debug("before autobaud\n");
+ if (options.flags & F_PARSE)
+ auto_baud(line_buf, sizeof(line_buf), &termios);
+
+ /* Set the optional timer */
+ alarm(options.timeout); /* if 0, alarm is not set */
+
+ /* Optionally wait for CR or LF before writing /etc/issue */
+ if (options.flags & F_WAITCRLF) {
+ char ch;
+
+ debug("waiting for cr-lf\n");
+ while (safe_read(STDIN_FILENO, &ch, 1) == 1) {
+ debug("read %x\n", (unsigned char)ch);
+ ch &= 0x7f; /* strip "parity bit" */
+ if (ch == '\n' || ch == '\r')
+ break;
+ }
+ }
+
+ logname = NULL;
+ if (!(options.flags & F_NOPROMPT)) {
+ /* NB:termios_init already set line speed
+ * to options.speeds[0] */
+ int baud_index = 0;
+
+ while (1) {
+ /* Read the login name. */
+ debug("reading login name\n");
+ logname = get_logname(line_buf, sizeof(line_buf),
+ &options, &chardata);
+ if (logname)
+ break;
+ /* we are here only if options.numspeed > 1 */
+ baud_index = (baud_index + 1) % options.numspeed;
+ cfsetispeed(&termios, options.speeds[baud_index]);
+ cfsetospeed(&termios, options.speeds[baud_index]);
+ tcsetattr_stdin_TCSANOW(&termios);
+ }
+ }
+
+ /* Disable timer. */
+ alarm(0);
+
+ /* Finalize the termios settings. */
+ termios_final(&options, &termios, &chardata);
+
+ /* Now the newline character should be properly written. */
+ full_write(STDOUT_FILENO, "\n", 1);
+
+ /* Let the login program take care of password validation. */
+ /* We use PATH because we trust that root doesn't set "bad" PATH,
+ * and getty is not suid-root applet. */
+ /* With -n, logname == NULL, and login will ask for username instead */
+ BB_EXECLP(options.login, options.login, "--", logname, NULL);
+ bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
}
diff --git a/release/src/router/busybox/loginutils/login.c b/release/src/router/busybox/loginutils/login.c
index c2bada25..d57d529c 100644
--- a/release/src/router/busybox/loginutils/login.c
+++ b/release/src/router/busybox/loginutils/login.c
@@ -1,481 +1,512 @@
/* vi: set sw=4 ts=4: */
-#include <fcntl.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
#include <syslog.h>
-#include <termios.h>
-#include <unistd.h>
#include <utmp.h>
#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <ctype.h>
-#include <time.h>
-
-#include "busybox.h"
-#ifdef CONFIG_SELINUX
-#include <flask_util.h>
-#include <get_sid_list.h>
-#include <proc_secure.h>
-#include <fs_secure.h>
-#endif
-#ifdef CONFIG_FEATURE_U_W_TMP
-// import from utmp.c
-static void checkutmp(int picky);
-static void setutmp(const char *name, const char *line);
-/* Stuff global to this file */
-struct utmp utent;
+#if ENABLE_SELINUX
+#include <selinux/selinux.h> /* for is_selinux_enabled() */
+#include <selinux/get_context_list.h> /* for get_default_context() */
+#include <selinux/flask.h> /* for security class definitions */
#endif
-// login defines
-#define TIMEOUT 60
-#define EMPTY_USERNAME_COUNT 10
-#define USERNAME_SIZE 32
+#if ENABLE_PAM
+/* PAM may include <locale.h>. We may need to undefine bbox's stub define: */
+#undef setlocale
+/* For some obscure reason, PAM is not in pam/xxx, but in security/xxx.
+ * Apparently they like to confuse people. */
+#include <security/pam_appl.h>
+#include <security/pam_misc.h>
+static const struct pam_conv conv = {
+ misc_conv,
+ NULL
+};
+#endif
+enum {
+ TIMEOUT = 60,
+ EMPTY_USERNAME_COUNT = 10,
+ USERNAME_SIZE = 32,
+ TTYNAME_SIZE = 32,
+};
-static int check_nologin ( int amroot );
+static char* short_tty;
-#if defined CONFIG_FEATURE_SECURETTY
-static int check_tty ( const char *tty );
+#if ENABLE_FEATURE_UTMP
+/* vv Taken from tinylogin utmp.c vv */
+/*
+ * read_or_build_utent - see if utmp file is correct for this process
+ *
+ * System V is very picky about the contents of the utmp file
+ * and requires that a slot for the current process exist.
+ * The utmp file is scanned for an entry with the same process
+ * ID. If no entry exists the process exits with a message.
+ *
+ * The "picky" flag is for network and other logins that may
+ * use special flags. It allows the pid checks to be overridden.
+ * This means that getty should never invoke login with any
+ * command line flags.
+ */
-#else
-static inline int check_tty ( const char *tty ) { return 1; }
+static void read_or_build_utent(struct utmp *utptr, int run_by_root)
+{
+ struct utmp *ut;
+ pid_t pid = getpid();
-#endif
+ setutent();
-static int is_my_tty ( const char *tty );
-static int login_prompt ( char *buf_name );
-static void motd ( void );
+ /* First, try to find a valid utmp entry for this process. */
+ /* If there is one, just use it. */
+ while ((ut = getutent()) != NULL)
+ if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0]
+ && (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS)
+ ) {
+ *utptr = *ut; /* struct copy */
+ if (run_by_root) /* why only for root? */
+ memset(utptr->ut_host, 0, sizeof(utptr->ut_host));
+ return;
+ }
+// Why? Do we require non-root to exec login from another
+// former login process (e.g. login shell)? Some login's have
+// login shells as children, so it won't work...
+// if (!run_by_root)
+// bb_error_msg_and_die("no utmp entry found");
+
+ /* Otherwise create a new one. */
+ memset(utptr, 0, sizeof(*utptr));
+ utptr->ut_type = LOGIN_PROCESS;
+ utptr->ut_pid = pid;
+ strncpy(utptr->ut_line, short_tty, sizeof(utptr->ut_line));
+ /* This one is only 4 chars wide. Try to fit something
+ * remotely meaningful by skipping "tty"... */
+ strncpy(utptr->ut_id, short_tty + 3, sizeof(utptr->ut_id));
+ strncpy(utptr->ut_user, "LOGIN", sizeof(utptr->ut_user));
+ utptr->ut_tv.tv_sec = time(NULL);
+}
-static void alarm_handler ( int sig )
+/*
+ * write_utent - put a USER_PROCESS entry in the utmp file
+ *
+ * write_utent changes the type of the current utmp entry to
+ * USER_PROCESS. the wtmp file will be updated as well.
+ */
+static void write_utent(struct utmp *utptr, const char *username)
{
- fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT );
- exit ( EXIT_SUCCESS );
+ utptr->ut_type = USER_PROCESS;
+ strncpy(utptr->ut_user, username, sizeof(utptr->ut_user));
+ utptr->ut_tv.tv_sec = time(NULL);
+ /* other fields already filled in by read_or_build_utent above */
+ setutent();
+ pututline(utptr);
+ endutent();
+#if ENABLE_FEATURE_WTMP
+ if (access(bb_path_wtmp_file, R_OK|W_OK) == -1) {
+ close(creat(bb_path_wtmp_file, 0664));
+ }
+ updwtmp(bb_path_wtmp_file, utptr);
+#endif
}
+#else /* !ENABLE_FEATURE_UTMP */
+#define read_or_build_utent(utptr, run_by_root) ((void)0)
+#define write_utent(utptr, username) ((void)0)
+#endif /* !ENABLE_FEATURE_UTMP */
-
-extern int login_main(int argc, char **argv)
+#if ENABLE_FEATURE_NOLOGIN
+static void die_if_nologin(void)
{
- char tty[BUFSIZ];
- char full_tty[200];
- char fromhost[512];
- char username[USERNAME_SIZE];
- char *tmp;
- int amroot;
- int flag;
- int failed;
- int count=0;
- struct passwd *pw, pw_copy;
-#ifdef CONFIG_WHEEL_GROUP
- struct group *grp;
-#endif
- int opt_preserve = 0;
- int opt_fflag = 0;
- char *opt_host = 0;
- int alarmstarted = 0;
-#ifdef CONFIG_SELINUX
- int flask_enabled = is_flask_enabled();
- security_id_t sid = 0, old_tty_sid, new_tty_sid;
+ FILE *fp;
+ int c;
+ int empty = 1;
+
+ fp = fopen_for_read("/etc/nologin");
+ if (!fp) /* assuming it does not exist */
+ return;
+
+ while ((c = getc(fp)) != EOF) {
+ if (c == '\n')
+ bb_putchar('\r');
+ bb_putchar(c);
+ empty = 0;
+ }
+ if (empty)
+ puts("\r\nSystem closed for routine maintenance\r");
+
+ fclose(fp);
+ fflush(NULL);
+ /* Users say that they do need this prior to exit: */
+ tcdrain(STDOUT_FILENO);
+ exit(EXIT_FAILURE);
+}
+#else
+static ALWAYS_INLINE void die_if_nologin(void) {}
#endif
- username[0]=0;
- amroot = ( getuid ( ) == 0 );
- signal ( SIGALRM, alarm_handler );
- alarm ( TIMEOUT );
- alarmstarted = 1;
-
- while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
- switch ( flag ) {
- case 'p':
- opt_preserve = 1;
- break;
- case 'f':
- /*
- * username must be a seperate token
- * (-f root, *NOT* -froot). --marekm
- */
- if ( optarg != argv[optind-1] )
- bb_show_usage( );
-
- if ( !amroot ) /* Auth bypass only if real UID is zero */
- bb_error_msg_and_die ( "-f permission denied" );
-
- safe_strncpy(username, optarg, USERNAME_SIZE);
- opt_fflag = 1;
- break;
- case 'h':
- opt_host = optarg;
+#if ENABLE_FEATURE_SECURETTY && !ENABLE_PAM
+static int check_securetty(void)
+{
+ char *buf = (char*)"/etc/securetty"; /* any non-NULL is ok */
+ parser_t *parser = config_open2("/etc/securetty", fopen_for_read);
+ while (config_read(parser, &buf, 1, 1, "# \t", PARSE_NORMAL)) {
+ if (strcmp(buf, short_tty) == 0)
break;
- default:
- bb_show_usage( );
- }
+ buf = NULL;
}
-
- if (optind < argc) // user from command line (getty)
- safe_strncpy(username, argv[optind], USERNAME_SIZE);
-
- if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 ))
- return EXIT_FAILURE; /* Must be a terminal */
-
-#ifdef CONFIG_FEATURE_U_W_TMP
- checkutmp ( !amroot );
-#endif
-
- tmp = ttyname ( 0 );
- if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 ))
- safe_strncpy ( tty, tmp + 5, sizeof( tty ));
- else
- safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
-
-#ifdef CONFIG_FEATURE_U_W_TMP
- if ( amroot )
- memset ( utent.ut_host, 0, sizeof utent.ut_host );
-#endif
-
- if ( opt_host ) {
-#ifdef CONFIG_FEATURE_U_W_TMP
- safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
+ config_close(parser);
+ /* buf != NULL here if config file was not found, empty
+ * or line was found which equals short_tty */
+ return buf != NULL;
+}
+#else
+static ALWAYS_INLINE int check_securetty(void) { return 1; }
#endif
- snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host );
- }
- else
- snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty );
-
- setpgrp();
-
- openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH );
-
- while ( 1 ) {
- failed = 0;
-
- if ( !username[0] )
- if(!login_prompt ( username ))
- return EXIT_FAILURE;
- if ( !alarmstarted && ( TIMEOUT > 0 )) {
- alarm ( TIMEOUT );
- alarmstarted = 1;
- }
-
- if (!( pw = getpwnam ( username ))) {
- pw_copy.pw_name = "UNKNOWN";
- pw_copy.pw_passwd = "!";
- opt_fflag = 0;
- failed = 1;
- } else
- pw_copy = *pw;
-
- pw = &pw_copy;
-
- if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' ))
- failed = 1;
-
- if ( opt_fflag ) {
- opt_fflag = 0;
- goto auth_ok;
- }
-
- if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty )))
- failed = 1;
-
- /* Don't check the password if password entry is empty (!) */
- if ( !pw-> pw_passwd[0] )
- goto auth_ok;
-
- /* authorization takes place here */
- if ( correct_password ( pw ))
- goto auth_ok;
-
- failed = 1;
-
-auth_ok:
- if ( !failed)
- break;
+#if ENABLE_SELINUX
+static void initselinux(char *username, char *full_tty,
+ security_context_t *user_sid)
+{
+ security_context_t old_tty_sid, new_tty_sid;
- { // delay next try
- time_t start, now;
-
- time ( &start );
- now = start;
- while ( difftime ( now, start ) < FAIL_DELAY) {
- sleep ( FAIL_DELAY );
- time ( &now );
- }
- }
+ if (!is_selinux_enabled())
+ return;
- puts("Login incorrect");
- username[0] = 0;
- if ( ++count == 3 ) {
- syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
- return EXIT_FAILURE;
+ if (get_default_context(username, NULL, user_sid)) {
+ bb_error_msg_and_die("cannot get SID for %s", username);
}
+ if (getfilecon(full_tty, &old_tty_sid) < 0) {
+ bb_perror_msg_and_die("getfilecon(%s) failed", full_tty);
}
-
- alarm ( 0 );
- if ( check_nologin ( pw-> pw_uid == 0 ))
- return EXIT_FAILURE;
-
-#ifdef CONFIG_FEATURE_U_W_TMP
- setutmp ( username, tty );
-#endif
-#ifdef CONFIG_SELINUX
- if (flask_enabled)
- {
- struct stat st;
-
- if (get_default_sid(username, 0, &sid))
- {
- fprintf(stderr, "Unable to get SID for %s\n", username);
- exit(1);
- }
- if (stat_secure(tty, &st, &old_tty_sid))
- {
- fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno));
- return EXIT_FAILURE;
- }
- if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
- {
- fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno));
- return EXIT_FAILURE;
- }
- if(chsid(tty, new_tty_sid) != 0)
- {
- fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno));
- return EXIT_FAILURE;
- }
+ if (security_compute_relabel(*user_sid, old_tty_sid,
+ SECCLASS_CHR_FILE, &new_tty_sid) != 0) {
+ bb_perror_msg_and_die("security_change_sid(%s) failed", full_tty);
+ }
+ if (setfilecon(full_tty, new_tty_sid) != 0) {
+ bb_perror_msg_and_die("chsid(%s, %s) failed", full_tty, new_tty_sid);
}
- else
- sid = 0;
-#endif
-
- if ( *tty != '/' )
- snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
- else
- safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
-
- if ( !is_my_tty ( full_tty ))
- syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
-
- /* Try these, but don't complain if they fail
- * (for example when the root fs is read only) */
- chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
- chmod ( full_tty, 0600 );
-
- change_identity ( pw );
- setup_environment ( pw-> pw_shell, 1, !opt_preserve, pw );
-
- motd ( );
- signal ( SIGALRM, SIG_DFL ); /* default alarm signal */
-
- if ( pw-> pw_uid == 0 )
- syslog ( LOG_INFO, "root login %s\n", fromhost );
-
- run_shell ( pw-> pw_shell, 1, 0, 0
-#ifdef CONFIG_SELINUX
- , sid
-#endif
- ); /* exec the shell finally. */
-
- return EXIT_FAILURE;
}
+#endif
-
-
-static int login_prompt ( char *buf_name )
+#if ENABLE_LOGIN_SCRIPTS
+static void run_login_script(struct passwd *pw, char *full_tty)
{
- char buf [1024];
- char *sp, *ep;
- int i;
-
- for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
- print_login_prompt();
-
- if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
- return 0;
-
- if ( !strchr ( buf, '\n' ))
- return 0;
-
- for ( sp = buf; isspace ( *sp ); sp++ ) { }
- for ( ep = sp; isgraph ( *ep ); ep++ ) { }
-
- *ep = 0;
- safe_strncpy(buf_name, sp, USERNAME_SIZE);
- if(buf_name[0])
- return 1;
+ char *t_argv[2];
+
+ t_argv[0] = getenv("LOGIN_PRE_SUID_SCRIPT");
+ if (t_argv[0]) {
+ t_argv[1] = NULL;
+ xsetenv("LOGIN_TTY", full_tty);
+ xsetenv("LOGIN_USER", pw->pw_name);
+ xsetenv("LOGIN_UID", utoa(pw->pw_uid));
+ xsetenv("LOGIN_GID", utoa(pw->pw_gid));
+ xsetenv("LOGIN_SHELL", pw->pw_shell);
+ spawn_and_wait(t_argv); /* NOMMU-friendly */
+ unsetenv("LOGIN_TTY");
+ unsetenv("LOGIN_USER");
+ unsetenv("LOGIN_UID");
+ unsetenv("LOGIN_GID");
+ unsetenv("LOGIN_SHELL");
}
- return 0;
}
+#else
+void run_login_script(struct passwd *pw, char *full_tty);
+#endif
-
-static int check_nologin ( int amroot )
+static void get_username_or_die(char *buf, int size_buf)
{
- if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
- FILE *fp;
- int c;
-
- if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
- while (( c = getc ( fp )) != EOF )
- putchar (( c == '\n' ) ? '\r' : c );
-
- fflush ( stdout );
- fclose ( fp );
- } else {
- puts ( "\r\nSystem closed for routine maintenance.\r" );
+ int c, cntdown;
+
+ cntdown = EMPTY_USERNAME_COUNT;
+ prompt:
+ print_login_prompt();
+ /* skip whitespace */
+ do {
+ c = getchar();
+ if (c == EOF) exit(EXIT_FAILURE);
+ if (c == '\n') {
+ if (!--cntdown) exit(EXIT_FAILURE);
+ goto prompt;
}
- if ( !amroot )
- return 1;
-
- puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
- }
- return 0;
+ } while (isspace(c));
+
+ *buf++ = c;
+ if (!fgets(buf, size_buf-2, stdin))
+ exit(EXIT_FAILURE);
+ if (!strchr(buf, '\n'))
+ exit(EXIT_FAILURE);
+ while (isgraph(*buf)) buf++;
+ *buf = '\0';
}
-#ifdef CONFIG_FEATURE_SECURETTY
-
-static int check_tty ( const char *tty )
+static void motd(void)
{
- FILE *fp;
- int i;
- char buf[BUFSIZ];
-
- if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
- while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
- for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
- if ( !isspace ( buf[i] ))
- break;
- }
- buf[++i] = '\0';
- if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
- continue;
-
- if ( strcmp ( buf, tty ) == 0 ) {
- fclose ( fp );
- return 1;
- }
- }
- fclose(fp);
- return 0;
+ int fd;
+
+ fd = open(bb_path_motd_file, O_RDONLY);
+ if (fd >= 0) {
+ fflush(stdout);
+ bb_copyfd_eof(fd, STDOUT_FILENO);
+ close(fd);
}
- /* A missing securetty file is not an error. */
- return 1;
}
-#endif
-
-/* returns 1 if true */
-static int is_my_tty ( const char *tty )
+static void alarm_handler(int sig UNUSED_PARAM)
{
- struct stat by_name, by_fd;
-
- if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
- return 0;
-
- if ( by_name. st_rdev != by_fd. st_rdev )
- return 0;
- else
- return 1;
+ /* This is the escape hatch! Poor serial line users and the like
+ * arrive here when their connection is broken.
+ * We don't want to block here */
+ ndelay_on(1);
+ printf("\r\nLogin timed out after %d seconds\r\n", TIMEOUT);
+ fflush(stdout);
+ /* unix API is brain damaged regarding O_NONBLOCK,
+ * we should undo it, or else we can affect other processes */
+ ndelay_off(1);
+ _exit(EXIT_SUCCESS);
}
-
-static void motd ( )
+int login_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int login_main(int argc UNUSED_PARAM, char **argv)
{
- FILE *fp;
- register int c;
+ enum {
+ LOGIN_OPT_f = (1<<0),
+ LOGIN_OPT_h = (1<<1),
+ LOGIN_OPT_p = (1<<2),
+ };
+ char *fromhost;
+ char username[USERNAME_SIZE];
+ const char *tmp;
+ int run_by_root;
+ unsigned opt;
+ int count = 0;
+ struct passwd *pw;
+ char *opt_host = opt_host; /* for compiler */
+ char *opt_user = opt_user; /* for compiler */
+ char *full_tty;
+ USE_SELINUX(security_context_t user_sid = NULL;)
+ USE_FEATURE_UTMP(struct utmp utent;)
+#if ENABLE_PAM
+ int pamret;
+ pam_handle_t *pamh;
+ const char *pamuser;
+ const char *failed_msg;
+ struct passwd pwdstruct;
+ char pwdbuf[256];
+#endif
- if (( fp = fopen ( bb_path_motd_file, "r" ))) {
- while (( c = getc ( fp )) != EOF )
- putchar ( c );
- fclose ( fp );
+ username[0] = '\0';
+ signal(SIGALRM, alarm_handler);
+ alarm(TIMEOUT);
+
+ /* More of suid paranoia if called by non-root: */
+ /* Clear dangerous stuff, set PATH */
+ run_by_root = !sanitize_env_if_suid();
+
+ /* Mandatory paranoia for suid applet:
+ * ensure that fd# 0,1,2 are opened (at least to /dev/null)
+ * and any extra open fd's are closed.
+ * (The name of the function is misleading. Not daemonizing here.) */
+ bb_daemonize_or_rexec(DAEMON_ONLY_SANITIZE | DAEMON_CLOSE_EXTRA_FDS, NULL);
+
+ opt = getopt32(argv, "f:h:p", &opt_user, &opt_host);
+ if (opt & LOGIN_OPT_f) {
+ if (!run_by_root)
+ bb_error_msg_and_die("-f is for root only");
+ safe_strncpy(username, opt_user, sizeof(username));
}
-}
+ argv += optind;
+ if (argv[0]) /* user from command line (getty) */
+ safe_strncpy(username, argv[0], sizeof(username));
+ /* Let's find out and memorize our tty */
+ if (!isatty(STDIN_FILENO) || !isatty(STDOUT_FILENO) || !isatty(STDERR_FILENO))
+ return EXIT_FAILURE; /* Must be a terminal */
+ full_tty = xmalloc_ttyname(STDIN_FILENO);
+ if (!full_tty)
+ full_tty = xstrdup("UNKNOWN");
+ short_tty = full_tty;
+ if (strncmp(full_tty, "/dev/", 5) == 0)
+ short_tty += 5;
+
+ read_or_build_utent(&utent, run_by_root);
+
+ if (opt & LOGIN_OPT_h) {
+ USE_FEATURE_UTMP(safe_strncpy(utent.ut_host, opt_host, sizeof(utent.ut_host));)
+ fromhost = xasprintf(" on '%s' from '%s'", short_tty, opt_host);
+ } else {
+ fromhost = xasprintf(" on '%s'", short_tty);
+ }
-#ifdef CONFIG_FEATURE_U_W_TMP
-// vv Taken from tinylogin utmp.c vv
-
-#define _WTMP_FILE "/var/log/wtmp"
-
-#define NO_UTENT \
- "No utmp entry. You must exec \"login\" from the lowest level \"sh\""
-#define NO_TTY \
- "Unable to determine your tty name."
-
-/*
- * checkutmp - see if utmp file is correct for this process
- *
- * System V is very picky about the contents of the utmp file
- * and requires that a slot for the current process exist.
- * The utmp file is scanned for an entry with the same process
- * ID. If no entry exists the process exits with a message.
- *
- * The "picky" flag is for network and other logins that may
- * use special flags. It allows the pid checks to be overridden.
- * This means that getty should never invoke login with any
- * command line flags.
- */
+ /* Was breaking "login <username>" from shell command line: */
+ /*bb_setpgrp();*/
-static void checkutmp(int picky)
-{
- char *line;
- struct utmp *ut;
- pid_t pid = getpid();
+ openlog(applet_name, LOG_PID | LOG_CONS, LOG_AUTH);
- setutent();
+ while (1) {
+ /* flush away any type-ahead (as getty does) */
+ ioctl(0, TCFLSH, TCIFLUSH);
- /* First, try to find a valid utmp entry for this process. */
- while ((ut = getutent()))
- if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
- (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
- break;
+ if (!username[0])
+ get_username_or_die(username, sizeof(username));
- /* If there is one, just use it, otherwise create a new one. */
- if (ut) {
- utent = *ut;
- } else {
- if (picky) {
- puts(NO_UTENT);
- exit(1);
+#if ENABLE_PAM
+ pamret = pam_start("login", username, &conv, &pamh);
+ if (pamret != PAM_SUCCESS) {
+ failed_msg = "start";
+ goto pam_auth_failed;
}
- line = ttyname(0);
- if (!line) {
- puts(NO_TTY);
- exit(1);
+ /* set TTY (so things like securetty work) */
+ pamret = pam_set_item(pamh, PAM_TTY, short_tty);
+ if (pamret != PAM_SUCCESS) {
+ failed_msg = "set_item(TTY)";
+ goto pam_auth_failed;
+ }
+ pamret = pam_authenticate(pamh, 0);
+ if (pamret != PAM_SUCCESS) {
+ failed_msg = "authenticate";
+ goto pam_auth_failed;
+ /* TODO: or just "goto auth_failed"
+ * since user seems to enter wrong password
+ * (in this case pamret == 7)
+ */
+ }
+ /* check that the account is healthy */
+ pamret = pam_acct_mgmt(pamh, 0);
+ if (pamret != PAM_SUCCESS) {
+ failed_msg = "acct_mgmt";
+ goto pam_auth_failed;
+ }
+ /* read user back */
+ 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 (!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("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 (!pw) {
+ strcpy(username, "UNKNOWN");
+ goto fake_it;
}
- if (strncmp(line, "/dev/", 5) == 0)
- line += 5;
- memset((void *) &utent, 0, sizeof utent);
- utent.ut_type = LOGIN_PROCESS;
- utent.ut_pid = pid;
- strncpy(utent.ut_line, line, sizeof utent.ut_line);
- /* XXX - assumes /dev/tty?? */
- strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
- strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
- time(&utent.ut_time);
- }
-}
-/*
- * setutmp - put a USER_PROCESS entry in the utmp file
- *
- * setutmp changes the type of the current utmp entry to
- * USER_PROCESS. the wtmp file will be updated as well.
- */
+ if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
+ goto auth_failed;
-static void setutmp(const char *name, const char *line)
-{
- utent.ut_type = USER_PROCESS;
- strncpy(utent.ut_user, name, sizeof utent.ut_user);
- time(&utent.ut_time);
- /* other fields already filled in by checkutmp above */
- setutent();
- pututline(&utent);
- endutent();
- updwtmp(_WTMP_FILE, &utent);
+ if (opt & LOGIN_OPT_f)
+ break; /* -f USER: success without asking passwd */
+
+ if (pw->pw_uid == 0 && !check_securetty())
+ goto auth_failed;
+
+ /* Don't check the password if password entry is empty (!) */
+ if (!pw->pw_passwd[0])
+ break;
+ fake_it:
+ /* authorization takes place here */
+ if (correct_password(pw))
+ break;
+#endif /* ENABLE_PAM */
+ auth_failed:
+ opt &= ~LOGIN_OPT_f;
+ bb_do_delay(FAIL_DELAY);
+ /* TODO: doesn't sound like correct English phrase to me */
+ puts("Login incorrect");
+ if (++count == 3) {
+ syslog(LOG_WARNING, "invalid password for '%s'%s",
+ username, fromhost);
+ return EXIT_FAILURE;
+ }
+ username[0] = '\0';
+ } /* while (1) */
+
+ alarm(0);
+ /* We can ignore /etc/nologin if we are logging in as root,
+ * it doesn't matter whether we are run by root or not */
+ if (pw->pw_uid != 0)
+ die_if_nologin();
+
+ write_utent(&utent, username);
+
+ USE_SELINUX(initselinux(username, full_tty, &user_sid));
+
+ /* Try these, but don't complain if they fail.
+ * _f_chown is safe wrt race t=ttyname(0);...;chown(t); */
+ fchown(0, pw->pw_uid, pw->pw_gid);
+ fchmod(0, 0600);
+
+ /* We trust environment only if we run by root */
+ if (ENABLE_LOGIN_SCRIPTS && run_by_root)
+ run_login_script(pw, full_tty);
+
+ change_identity(pw);
+ tmp = pw->pw_shell;
+ if (!tmp || !*tmp)
+ tmp = DEFAULT_SHELL;
+ /* setup_environment params: shell, clear_env, change_env, pw */
+ setup_environment(tmp, !(opt & LOGIN_OPT_p), 1, pw);
+
+ motd();
+
+ if (pw->pw_uid == 0)
+ syslog(LOG_INFO, "root login%s", fromhost);
+
+ /* well, a simple setexeccon() here would do the job as well,
+ * but let's play the game for now */
+ USE_SELINUX(set_current_security_context(user_sid);)
+
+ // util-linux login also does:
+ // /* start new session */
+ // setsid();
+ // /* TIOCSCTTY: steal tty from other process group */
+ // if (ioctl(0, TIOCSCTTY, 1)) error_msg...
+ // BBox login used to do this (see above):
+ // bb_setpgrp();
+ // If this stuff is really needed, add it and explain why!
+
+ /* Set signals to defaults */
+ /* Non-ignored signals revert to SIG_DFL on exec anyway */
+ /*signal(SIGALRM, SIG_DFL);*/
+
+ /* Is this correct? This way user can ctrl-c out of /etc/profile,
+ * potentially creating security breach (tested with bash 3.0).
+ * But without this, bash 3.0 will not enable ctrl-c either.
+ * Maybe bash is buggy?
+ * Need to find out what standards say about /bin/login -
+ * should we leave SIGINT etc enabled or disabled? */
+ signal(SIGINT, SIG_DFL);
+
+ /* Exec login shell with no additional parameters */
+ run_shell(tmp, 1, NULL, NULL);
+
+ /* return EXIT_FAILURE; - not reached */
}
-#endif /* CONFIG_FEATURE_U_W_TMP */
diff --git a/release/src/router/busybox/loginutils/passwd.c b/release/src/router/busybox/loginutils/passwd.c
index e8577066..7b93713b 100644
--- a/release/src/router/busybox/loginutils/passwd.c
+++ b/release/src/router/busybox/loginutils/passwd.c
@@ -1,406 +1,204 @@
/* vi: set sw=4 ts=4: */
-#include <fcntl.h>
-#include <stdio.h>
-#include <string.h>
-#include <signal.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <unistd.h>
-#include <utime.h>
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+#include "libbb.h"
#include <syslog.h>
-#include <time.h>
-#include <sys/resource.h>
-#include <errno.h>
-#include "busybox.h"
-
-static char crypt_passwd[128];
-
-static int create_backup(const char *backup, FILE * fp);
-static int new_password(const struct passwd *pw, int amroot, int algo);
-static void set_filesize_limit(int blocks);
-
-
-int get_algo(char *a)
+static void nuke_str(char *str)
{
- int x = 1; /* standard: MD5 */
-
- if (strcasecmp(a, "des") == 0)
- x = 0;
- return x;
+ if (str) memset(str, 0, strlen(str));
}
-
-extern int update_passwd(const struct passwd *pw, char *crypt_pw)
+static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
{
- char filename[1024];
- char buf[1025];
- char buffer[80];
- char username[32];
- char *pw_rest;
- int has_shadow = 0;
- int mask;
- int continued;
- FILE *fp;
- FILE *out_fp;
- struct stat sb;
- struct flock lock;
-
- if (access(bb_path_shadow_file, F_OK) == 0) {
- has_shadow = 1;
- }
- if (has_shadow) {
- snprintf(filename, sizeof filename, "%s", bb_path_shadow_file);
- } else {
- snprintf(filename, sizeof filename, "%s", bb_path_passwd_file);
- }
-
- if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) {
- /* return 0; */
- return 1;
- }
-
- /* Lock the password file before updating */
- lock.l_type = F_WRLCK;
- lock.l_whence = SEEK_SET;
- lock.l_start = 0;
- lock.l_len = 0;
- if (fcntl(fileno(fp), F_SETLK, &lock) < 0) {
- fprintf(stderr, "%s: %s\n", filename, strerror(errno));
- return 1;
- }
- lock.l_type = F_UNLCK;
-
- snprintf(buf, sizeof buf, "%s-", filename);
- if (create_backup(buf, fp)) {
- fcntl(fileno(fp), F_SETLK, &lock);
- fclose(fp);
- return 1;
- }
- snprintf(buf, sizeof buf, "%s+", filename);
- mask = umask(0777);
- out_fp = fopen(buf, "w");
- umask(mask);
- if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777))
- || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) {
- fcntl(fileno(fp), F_SETLK, &lock);
- fclose(fp);
- fclose(out_fp);
- return 1;
- }
-
- continued = 0;
- snprintf(username, sizeof username, "%s:", pw->pw_name);
- rewind(fp);
- while (!feof(fp)) {
- fgets(buffer, sizeof buffer, fp);
- if (!continued) { // Check to see if we're updating this line.
- if (strncmp(username, buffer, strlen(username)) == 0) { // we have a match.
- pw_rest = strchr(buffer, ':');
- *pw_rest++ = '\0';
- pw_rest = strchr(pw_rest, ':');
- fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest);
- } else {
- fputs(buffer, out_fp);
- }
- } else {
- fputs(buffer, out_fp);
- }
- if (buffer[strlen(buffer) - 1] == '\n') {
- continued = 0;
- } else {
- continued = 1;
+ char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */
+ char *orig = (char*)"";
+ char *newp = NULL;
+ char *cp = NULL;
+ char *ret = NULL; /* failure so far */
+
+ if (myuid && pw->pw_passwd[0]) {
+ char *encrypted;
+
+ orig = bb_ask_stdin("Old password:"); /* returns ptr to static */
+ if (!orig)
+ goto err_ret;
+ encrypted = pw_encrypt(orig, pw->pw_passwd, 1); /* returns malloced str */
+ if (strcmp(encrypted, pw->pw_passwd) != 0) {
+ syslog(LOG_WARNING, "incorrect password for %s",
+ pw->pw_name);
+ bb_do_delay(FAIL_DELAY);
+ puts("Incorrect password");
+ goto err_ret;
}
- bzero(buffer, sizeof buffer);
- }
-
- if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) {
- unlink(buf);
- fcntl(fileno(fp), F_SETLK, &lock);
- fclose(fp);
- return 1;
- }
- if (rename(buf, filename) < 0) {
- fcntl(fileno(fp), F_SETLK, &lock);
- fclose(fp);
- return 1;
- } else {
- fcntl(fileno(fp), F_SETLK, &lock);
- fclose(fp);
- return 0;
- }
+ if (ENABLE_FEATURE_CLEAN_UP) free(encrypted);
+ }
+ orig = xstrdup(orig); /* or else bb_ask_stdin() will destroy it */
+ newp = bb_ask_stdin("New password:"); /* returns ptr to static */
+ if (!newp)
+ goto err_ret;
+ newp = xstrdup(newp); /* we are going to bb_ask_stdin() again, so save it */
+ if (ENABLE_FEATURE_PASSWD_WEAK_CHECK
+ && obscure(orig, newp, pw) && myuid)
+ goto err_ret; /* non-root is not allowed to have weak passwd */
+
+ cp = bb_ask_stdin("Retype password:");
+ if (!cp)
+ goto err_ret;
+ if (strcmp(cp, newp)) {
+ puts("Passwords don't match");
+ goto err_ret;
+ }
+
+ crypt_make_salt(salt, 1, 0); /* des */
+ if (algo) { /* MD5 */
+ strcpy(salt, "$1$");
+ crypt_make_salt(salt + 3, 4, 0);
+ }
+ /* pw_encrypt returns malloced str */
+ ret = pw_encrypt(newp, salt, 1);
+ /* whee, success! */
+
+ err_ret:
+ nuke_str(orig);
+ if (ENABLE_FEATURE_CLEAN_UP) free(orig);
+ nuke_str(newp);
+ if (ENABLE_FEATURE_CLEAN_UP) free(newp);
+ nuke_str(cp);
+ return ret;
}
-
-extern int passwd_main(int argc, char **argv)
+int passwd_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int passwd_main(int argc UNUSED_PARAM, char **argv)
{
- int amroot;
- char *cp;
- char *np;
- char *name;
+ enum {
+ OPT_algo = 0x1, /* -a - password algorithm */
+ OPT_lock = 0x2, /* -l - lock account */
+ OPT_unlock = 0x4, /* -u - unlock account */
+ OPT_delete = 0x8, /* -d - delete password */
+ OPT_lud = 0xe,
+ STATE_ALGO_md5 = 0x10,
+ //STATE_ALGO_des = 0x20, not needed yet
+ };
+ unsigned opt;
+ int rc;
+ const char *opt_a = "";
+ const char *filename;
char *myname;
- int flag;
- int algo = 0; /* -a - password algorithm */
- int lflg = 0; /* -l - lock account */
- int uflg = 0; /* -u - unlock account */
- int dflg = 0; /* -d - delete password */
- const struct passwd *pw;
- unsigned short ruid;
-
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- const struct spwd *sp;
-#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
- amroot = (getuid() == 0);
- openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
- while ((flag = getopt(argc, argv, "a:dlu")) != EOF) {
- switch (flag) {
- case 'a':
- algo = get_algo(optarg);
- break;
- case 'd':
- dflg++;
- break;
- case 'l':
- lflg++;
- break;
- case 'u':
- uflg++;
- break;
- default:
- bb_show_usage();
- }
- }
- ruid = getuid();
- pw = (struct passwd *) getpwuid(ruid);
- if (!pw) {
- bb_error_msg_and_die("Cannot determine your user name.");
- }
- myname = (char *) bb_xstrdup(pw->pw_name);
- if (optind < argc) {
- name = argv[optind];
- } else {
- name = myname;
- }
- if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) {
+ char *name;
+ char *newp;
+ struct passwd *pw;
+ uid_t myuid;
+ struct rlimit rlimit_fsize;
+ char c;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* Using _r function to avoid pulling in static buffers */
+ struct spwd spw;
+ char buffer[256];
+#endif
+
+ logmode = LOGMODE_BOTH;
+ openlog(applet_name, 0, LOG_AUTH);
+ opt = getopt32(argv, "a:lud", &opt_a);
+ //argc -= optind;
+ argv += optind;
+
+ if (strcasecmp(opt_a, "des") != 0) /* -a */
+ opt |= STATE_ALGO_md5;
+ //else
+ // opt |= STATE_ALGO_des;
+ myuid = getuid();
+ /* -l, -u, -d require root priv and username argument */
+ if ((opt & OPT_lud) && (myuid || !argv[0]))
bb_show_usage();
- }
- pw = getpwnam(name);
- if (!pw) {
- bb_error_msg_and_die("Unknown user %s\n", name);
- }
- if (!amroot && pw->pw_uid != getuid()) {
- syslog(LOG_WARNING, "can't change pwd for `%s'", name);
- bb_error_msg_and_die("Permission denied.\n");
- }
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- sp = getspnam(name);
- if (!sp) {
- sp = (struct spwd *) pwd_to_spwd(pw);
- }
- cp = sp->sp_pwdp;
- np = sp->sp_namp;
-#else
- cp = pw->pw_passwd;
- np = name;
-#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
-
- safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
- if (!(dflg || lflg || uflg)) {
- if (!amroot) {
- if (cp[0] == '!') {
- syslog(LOG_WARNING, "password locked for `%s'", np);
- bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np);
- }
- }
- printf("Changing password for %s\n", name);
- if (new_password(pw, amroot, algo)) {
- bb_error_msg_and_die( "The password for %s is unchanged.\n", name);
- }
- } else if (lflg) {
- if (crypt_passwd[0] != '!') {
- memmove(&crypt_passwd[1], crypt_passwd,
- sizeof crypt_passwd - 1);
- crypt_passwd[sizeof crypt_passwd - 1] = '\0';
- crypt_passwd[0] = '!';
- }
- } else if (uflg) {
- if (crypt_passwd[0] == '!') {
- memmove(crypt_passwd, &crypt_passwd[1],
- sizeof crypt_passwd - 1);
- }
- } else if (dflg) {
- crypt_passwd[0] = '\0';
- }
- set_filesize_limit(30000);
- signal(SIGHUP, SIG_IGN);
- signal(SIGINT, SIG_IGN);
- signal(SIGQUIT, SIG_IGN);
- umask(077);
- if (setuid(0)) {
- syslog(LOG_ERR, "can't setuid(0)");
- bb_error_msg_and_die( "Cannot change ID to root.\n");
- }
- if (!update_passwd(pw, crypt_passwd)) {
- syslog(LOG_INFO, "password for `%s' changed by user `%s'", name,
- myname);
- printf("Password changed.\n");
- } else {
- syslog(LOG_WARNING, "an error occurred updating the password file");
- bb_error_msg_and_die("An error occurred updating the password file.\n");
- }
- return (0);
-}
-
+ /* Will complain and die if username not found */
+ myname = xstrdup(xuid2uname(myuid));
+ name = argv[0] ? argv[0] : myname;
-static int create_backup(const char *backup, FILE * fp)
-{
- struct stat sb;
- struct utimbuf ub;
- FILE *bkfp;
- int c, mask;
-
- if (fstat(fileno(fp), &sb))
- /* return -1; */
- return 1;
-
- mask = umask(077);
- bkfp = fopen(backup, "w");
- umask(mask);
- if (!bkfp)
- /* return -1; */
- return 1;
-
- /* TODO: faster copy, not one-char-at-a-time. --marekm */
- rewind(fp);
- while ((c = getc(fp)) != EOF) {
- if (putc(c, bkfp) == EOF)
- break;
- }
- if (c != EOF || fflush(bkfp)) {
- fclose(bkfp);
- /* return -1; */
- return 1;
+ pw = xgetpwnam(name);
+ if (myuid && pw->pw_uid != myuid) {
+ /* LOGMODE_BOTH */
+ bb_error_msg_and_die("%s can't change password for %s", myname, name);
}
- if (fclose(bkfp))
- /* return -1; */
- return 1;
-
- ub.actime = sb.st_atime;
- ub.modtime = sb.st_mtime;
- utime(backup, &ub);
- return 0;
-}
-
-static int i64c(int i)
-{
- if (i <= 0)
- return ('.');
- if (i == 1)
- return ('/');
- if (i >= 2 && i < 12)
- return ('0' - 2 + i);
- if (i >= 12 && i < 38)
- return ('A' - 12 + i);
- if (i >= 38 && i < 63)
- return ('a' - 38 + i);
- return ('z');
-}
-
-static char *crypt_make_salt(void)
-{
- time_t now;
- static unsigned long x;
- static char result[3];
-
- time(&now);
- x += now + getpid() + clock();
- result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
- result[1] = i64c(((x >> 12) ^ x) & 077);
- result[2] = '\0';
- return result;
-}
-
-static int new_password(const struct passwd *pw, int amroot, int algo)
-{
- char *clear;
- char *cipher;
- char *cp;
- char orig[200];
- char pass[200];
- time_t start, now;
-
- if (!amroot && crypt_passwd[0]) {
- if (!(clear = getpass("Old password:"))) {
- /* return -1; */
- return 1;
- }
- cipher = pw_encrypt(clear, crypt_passwd);
- if (strcmp(cipher, crypt_passwd) != 0) {
- syslog(LOG_WARNING, "incorrect password for `%s'",
- pw->pw_name);
- time(&start);
- now = start;
- while (difftime(now, start) < FAIL_DELAY) {
- sleep(FAIL_DELAY);
- time(&now);
- }
- fprintf(stderr, "Incorrect password.\n");
- /* return -1; */
- return 1;
- }
- safe_strncpy(orig, clear, sizeof(orig));
- bzero(clear, strlen(clear));
- bzero(cipher, strlen(cipher));
- } else {
- orig[0] = '\0';
- }
- if (! (cp=getpass("Enter the new password (minimum of 5, maximum of 8 characters)\n"
- "Please use a combination of upper and lower case letters and numbers.\n"
- "Enter new password: ")))
+#if ENABLE_FEATURE_SHADOWPASSWDS
{
- bzero(orig, sizeof orig);
- /* return -1; */
- return 1;
- }
- safe_strncpy(pass, cp, sizeof(pass));
- bzero(cp, strlen(cp));
- /* if (!obscure(orig, pass, pw)) { */
- if (obscure(orig, pass, pw)) {
- if (amroot) {
- printf("\nWarning: weak password (continuing).\n");
+ /* getspnam_r may return 0 yet set result to NULL.
+ * At least glibc 2.4 does this. Be extra paranoid here. */
+ struct spwd *result = NULL;
+ if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result)
+ || !result || strcmp(result->sp_namp, pw->pw_name) != 0) {
+ /* LOGMODE_BOTH */
+ bb_error_msg("no record of %s in %s, using %s",
+ name, bb_path_shadow_file,
+ bb_path_passwd_file);
} else {
- /* return -1; */
- return 1;
+ pw->pw_passwd = result->sp_pwdp;
}
}
- if (!(cp = getpass("Re-enter new password: "))) {
- bzero(orig, sizeof orig);
- /* return -1; */
- return 1;
- }
- if (strcmp(cp, pass)) {
- fprintf(stderr, "Passwords do not match.\n");
- /* return -1; */
- return 1;
- }
- bzero(cp, strlen(cp));
- bzero(orig, sizeof(orig));
-
- if (algo == 1) {
- cp = pw_encrypt(pass, "$1$");
- } else
- cp = pw_encrypt(pass, crypt_make_salt());
- bzero(pass, sizeof pass);
- safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
- return 0;
-}
+#endif
-static void set_filesize_limit(int blocks)
-{
- struct rlimit rlimit_fsize;
-
- rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks;
+ /* Decide what the new password will be */
+ newp = NULL;
+ c = pw->pw_passwd[0] - '!';
+ if (!(opt & OPT_lud)) {
+ if (myuid && !c) { /* passwd starts with '!' */
+ /* LOGMODE_BOTH */
+ bb_error_msg_and_die("cannot change "
+ "locked password for %s", name);
+ }
+ printf("Changing password for %s\n", name);
+ newp = new_password(pw, myuid, opt & STATE_ALGO_md5);
+ if (!newp) {
+ logmode = LOGMODE_STDIO;
+ bb_error_msg_and_die("password for %s is unchanged", name);
+ }
+ } else if (opt & OPT_lock) {
+ if (!c) goto skip; /* passwd starts with '!' */
+ newp = xasprintf("!%s", pw->pw_passwd);
+ } else if (opt & OPT_unlock) {
+ if (c) goto skip; /* not '!' */
+ /* pw->pw_passwd points to static storage,
+ * strdup'ing to avoid nasty surprizes */
+ newp = xstrdup(&pw->pw_passwd[1]);
+ } else if (opt & OPT_delete) {
+ //newp = xstrdup("");
+ newp = (char*)"";
+ }
+
+ rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
+ bb_signals(0
+ + (1 << SIGHUP)
+ + (1 << SIGINT)
+ + (1 << SIGQUIT)
+ , SIG_IGN);
+ umask(077);
+ xsetuid(0);
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ filename = bb_path_shadow_file;
+ rc = update_passwd(bb_path_shadow_file, name, newp, NULL);
+ if (rc == 0) /* no lines updated, no errors detected */
+#endif
+ {
+ filename = bb_path_passwd_file;
+ rc = update_passwd(bb_path_passwd_file, name, newp, NULL);
+ }
+ /* LOGMODE_BOTH */
+ if (rc < 0)
+ bb_error_msg_and_die("cannot update password file %s",
+ filename);
+ bb_info_msg("Password for %s changed by %s", name, myname);
+
+ //if (ENABLE_FEATURE_CLEAN_UP) free(newp);
+ skip:
+ if (!newp) {
+ bb_error_msg_and_die("password for %s is already %slocked",
+ name, (opt & OPT_unlock) ? "un" : "");
+ }
+ if (ENABLE_FEATURE_CLEAN_UP) free(myname);
+ return 0;
}
diff --git a/release/src/router/busybox/loginutils/su.c b/release/src/router/busybox/loginutils/su.c
index 85f5cbe7..de8c18d2 100644
--- a/release/src/router/busybox/loginutils/su.c
+++ b/release/src/router/busybox/loginutils/su.c
@@ -1,166 +1,100 @@
/* vi: set sw=4 ts=4: */
+/*
+ * Mini su implementation for busybox
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
-#include <fcntl.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+#include "libbb.h"
#include <syslog.h>
-#include <termios.h>
-#include <unistd.h>
-#include <utmp.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <ctype.h>
-#include <time.h>
-#include "busybox.h"
+#define SU_OPT_mp (3)
+#define SU_OPT_l (4)
-
-
-/* The shell to run if none is given in the user's passwd entry. */
-#define DEFAULT_SHELL "/bin/sh"
-#define DEFAULT_USER "root"
-
-//#define SYSLOG_SUCCESS
-#define SYSLOG_FAILURE
-
-
-#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
-/* Log the fact that someone has run su to the user given by PW;
- if SUCCESSFUL is nonzero, they gave the correct password, etc. */
-
-static void log_su ( const struct passwd *pw, int successful )
+int su_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int su_main(int argc UNUSED_PARAM, char **argv)
{
- const char *old_user, *tty;
-
-#if !defined( SYSLOG_SUCESS )
- if ( successful )
- return;
-#endif
-#if !defined( SYSLOG_FAILURE )
- if ( !successful )
- return;
-#endif
-
- if ( pw-> pw_uid ) // not to root -> ignored
- return;
-
- /* The utmp entry (via getlogin) is probably the best way to identify
- the user, especially if someone su's from a su-shell. */
- old_user = getlogin ( );
- if ( !old_user ) {
- /* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid. */
- struct passwd *pwd = getpwuid ( getuid ( ));
- old_user = ( pwd ? pwd-> pw_name : "" );
+ unsigned flags;
+ char *opt_shell = NULL;
+ char *opt_command = NULL;
+ const char *opt_username = "root";
+ struct passwd *pw;
+ uid_t cur_uid = getuid();
+ const char *tty;
+ char *old_user;
+
+ flags = getopt32(argv, "mplc:s:", &opt_command, &opt_shell);
+ //argc -= optind;
+ argv += optind;
+
+ if (argv[0] && LONE_DASH(argv[0])) {
+ flags |= SU_OPT_l;
+ argv++;
}
-
- tty = ttyname ( 2 );
-
- openlog ( "su", 0, LOG_AUTH );
- syslog ( LOG_NOTICE, "%s%s on %s", successful ? "" : "FAILED SU ", old_user, tty ? tty : "none" );
-}
-#endif
-
+ /* get user if specified */
+ if (argv[0]) {
+ opt_username = argv[0];
+ argv++;
+ }
-int su_main ( int argc, char **argv )
-{
- int flag;
- int opt_preserve = 0;
- int opt_loginshell = 0;
- char *opt_shell = 0;
- char *opt_command = 0;
- char *opt_username = DEFAULT_USER;
- char **opt_args = 0;
- struct passwd *pw, pw_copy;
-
-
- while (( flag = getopt ( argc, argv, "c:lmps:" )) != -1 ) {
- switch ( flag ) {
- case 'c':
- opt_command = optarg;
- break;
- case 'm':
- case 'p':
- opt_preserve = 1;
- break;
- case 's':
- opt_shell = optarg;
- break;
- case 'l':
- opt_loginshell = 1;
- break;
- default:
- bb_show_usage( );
- break;
- }
+ if (ENABLE_FEATURE_SU_SYSLOG) {
+ /* The utmp entry (via getlogin) is probably the best way to identify
+ the user, especially if someone su's from a su-shell.
+ But getlogin can fail -- usually due to lack of utmp entry.
+ in this case resort to getpwuid. */
+ old_user = xstrdup(USE_FEATURE_UTMP(getlogin() ? : ) (pw = getpwuid(cur_uid)) ? pw->pw_name : "");
+ tty = xmalloc_ttyname(2) ? : "none";
+ openlog(applet_name, 0, LOG_AUTH);
}
- if (( optind < argc ) && ( argv [optind][0] == '-' ) && ( argv [optind][1] == 0 )) {
- opt_loginshell = 1;
- ++optind;
- }
+ pw = xgetpwnam(opt_username);
- /* get user if specified */
- if ( optind < argc )
- opt_username = argv [optind++];
-
- if ( optind < argc )
- opt_args = argv + optind;
-
-
- pw = getpwnam ( opt_username );
- if ( !pw )
- bb_error_msg_and_die ( "user %s does not exist", opt_username );
-
/* Make sure pw->pw_shell is non-NULL. It may be NULL when NEW_USER
is a username that is retrieved via NIS (YP), but that doesn't have
a default shell listed. */
- if ( !pw-> pw_shell || !pw->pw_shell [0] )
- pw-> pw_shell = (char *) DEFAULT_SHELL;
-
- /* Make a copy of the password information and point pw at the local
- copy instead. Otherwise, some systems (e.g. Linux) would clobber
- the static data through the getlogin call from log_su. */
- pw_copy = *pw;
- pw = &pw_copy;
- pw-> pw_name = bb_xstrdup ( pw-> pw_name );
- pw-> pw_dir = bb_xstrdup ( pw-> pw_dir );
- pw-> pw_shell = bb_xstrdup ( pw-> pw_shell );
-
- if (( getuid ( ) == 0 ) || correct_password ( pw ))
- log_su ( pw, 1 );
- else {
- log_su ( pw, 0 );
- bb_error_msg_and_die ( "incorrect password" );
+ if (!pw->pw_shell || !pw->pw_shell[0])
+ pw->pw_shell = (char *)DEFAULT_SHELL;
+
+ if ((cur_uid == 0) || correct_password(pw)) {
+ if (ENABLE_FEATURE_SU_SYSLOG)
+ syslog(LOG_NOTICE, "%c %s %s:%s",
+ '+', tty, old_user, opt_username);
+ } else {
+ if (ENABLE_FEATURE_SU_SYSLOG)
+ syslog(LOG_NOTICE, "%c %s %s:%s",
+ '-', tty, old_user, opt_username);
+ bb_error_msg_and_die("incorrect password");
+ }
+
+ if (ENABLE_FEATURE_CLEAN_UP && ENABLE_FEATURE_SU_SYSLOG) {
+ closelog();
+ free(old_user);
}
- if ( !opt_shell && opt_preserve )
- opt_shell = getenv ( "SHELL" );
+ if (!opt_shell && (flags & SU_OPT_mp))
+ opt_shell = getenv("SHELL");
- if ( opt_shell && getuid ( ) && restricted_shell ( pw-> pw_shell ))
- {
+#if ENABLE_FEATURE_SU_CHECKS_SHELLS
+ if (opt_shell && cur_uid && restricted_shell(pw->pw_shell)) {
/* The user being su'd to has a nonstandard shell, and so is
probably a uucp account or has restricted access. Don't
compromise the account by allowing access with a standard
shell. */
- fputs ( "using restricted shell\n", stderr );
- opt_shell = 0;
+ bb_error_msg("using restricted shell");
+ opt_shell = NULL;
}
+#endif
+ if (!opt_shell)
+ opt_shell = pw->pw_shell;
- if ( !opt_shell )
- opt_shell = bb_xstrdup ( pw-> pw_shell );
+ change_identity(pw);
+ /* setup_environment params: shell, clear_env, change_env, pw */
+ setup_environment(opt_shell, flags & SU_OPT_l, !(flags & SU_OPT_mp), pw);
+ USE_SELINUX(set_current_security_context(NULL);)
- change_identity ( pw );
- setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw );
- run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args
-#ifdef CONFIG_SELINUX
- , 0
-#endif
- );
-
- return EXIT_FAILURE;
+ /* Never returns */
+ run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
+
+ /* return EXIT_FAILURE; - not reached */
}
diff --git a/release/src/router/busybox/loginutils/sulogin.c b/release/src/router/busybox/loginutils/sulogin.c
index bb4716e0..4ffefe93 100644
--- a/release/src/router/busybox/loginutils/sulogin.c
+++ b/release/src/router/busybox/loginutils/sulogin.c
@@ -1,184 +1,117 @@
/* vi: set sw=4 ts=4: */
-#include <fcntl.h>
-#include <signal.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
+/*
+ * Mini sulogin implementation for busybox
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "libbb.h"
#include <syslog.h>
-#include <termios.h>
-#include <unistd.h>
-#include <utmp.h>
-#include <sys/resource.h>
-#include <sys/stat.h>
-#include <sys/time.h>
-#include <sys/types.h>
-#include <ctype.h>
-#include <time.h>
-
-#include "busybox.h"
-
-
-// sulogin defines
-#define SULOGIN_PROMPT "\nGive root password for system maintenance\n" \
- "(or type Control-D for normal startup):"
-
-static const char *forbid[] = {
- "ENV",
- "BASH_ENV",
- "HOME",
- "IFS",
- "PATH",
- "SHELL",
- "LD_LIBRARY_PATH",
- "LD_PRELOAD",
- "LD_TRACE_LOADED_OBJECTS",
- "LD_BIND_NOW",
- "LD_AOUT_LIBRARY_PATH",
- "LD_AOUT_PRELOAD",
- "LD_NOWARN",
- "LD_KEEPDIR",
- (char *) 0
-};
-
-
-
-static void catchalarm(int junk)
-{
- exit(EXIT_FAILURE);
-}
+//static void catchalarm(int UNUSED_PARAM junk)
+//{
+// exit(EXIT_FAILURE);
+//}
-extern int sulogin_main(int argc, char **argv)
+
+int sulogin_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int sulogin_main(int argc UNUSED_PARAM, char **argv)
{
char *cp;
- char *device = (char *) 0;
- const char *name = "root";
int timeout = 0;
- static char pass[BUFSIZ];
- struct termios termio;
- struct passwd pwent;
struct passwd *pwd;
- time_t start, now;
- const char **p;
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- struct spwd *spwd = NULL;
-#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
-
- tcgetattr(0, &termio);
- /* set control chars */
- termio.c_cc[VINTR] = 3; /* C-c */
- termio.c_cc[VQUIT] = 28; /* C-\ */
- termio.c_cc[VERASE] = 127; /* C-? */
- termio.c_cc[VKILL] = 21; /* C-u */
- termio.c_cc[VEOF] = 4; /* C-d */
- termio.c_cc[VSTART] = 17; /* C-q */
- termio.c_cc[VSTOP] = 19; /* C-s */
- termio.c_cc[VSUSP] = 26; /* C-z */
- /* use line dicipline 0 */
- termio.c_line = 0;
- /* Make it be sane */
- termio.c_cflag &= CBAUD|CBAUDEX|CSIZE|CSTOPB|PARENB|PARODD;
- termio.c_cflag |= CREAD|HUPCL|CLOCAL;
- /* input modes */
- termio.c_iflag = ICRNL | IXON | IXOFF;
- /* output modes */
- termio.c_oflag = OPOST | ONLCR;
- /* local modes */
- termio.c_lflag = ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHOCTL | ECHOKE | IEXTEN;
- tcsetattr(0, TCSANOW, &termio);
- openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
- if (argc > 1) {
- if (strncmp(argv[1], "-t", 2) == 0) {
- if (strcmp(argv[1], "-t") == 0) {
- if (argc > 2) {
- timeout = atoi(argv[2]);
- if (argc > 3) {
- device = argv[3];
- }
- }
- } else {
- if (argc > 2) {
- device = argv[2];
- }
- }
- } else {
- device = argv[1];
- }
- if (device) {
- close(0);
- close(1);
- close(2);
- if (open(device, O_RDWR) >= 0) {
- dup(0);
- dup(0);
- } else {
- syslog(LOG_WARNING, "cannot open %s\n", device);
- exit(EXIT_FAILURE);
- }
- }
- }
- if (access(bb_path_passwd_file, 0) == -1) {
- syslog(LOG_WARNING, "No password file\n");
- bb_error_msg_and_die("No password file\n");
+ const char *shell;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* Using _r function to avoid pulling in static buffers */
+ char buffer[256];
+ struct spwd spw;
+#endif
+
+ logmode = LOGMODE_BOTH;
+ openlog(applet_name, 0, LOG_AUTH);
+
+ opt_complementary = "t+"; /* -t N */
+ getopt32(argv, "t:", &timeout);
+ argv += optind;
+
+ if (argv[0]) {
+ close(0);
+ close(1);
+ dup(xopen(argv[0], O_RDWR));
+ close(2);
+ dup(0);
}
+
+ /* Malicious use like "sulogin /dev/sda"? */
if (!isatty(0) || !isatty(1) || !isatty(2)) {
- exit(EXIT_FAILURE);
+ logmode = LOGMODE_SYSLOG;
+ bb_error_msg_and_die("not a tty");
}
+ /* Clear dangerous stuff, set PATH */
+ sanitize_env_if_suid();
- /* Clear out anything dangerous from the environment */
- for (p = forbid; *p; p++)
- unsetenv(*p);
+// bb_ask() already handles this
+// signal(SIGALRM, catchalarm);
-
- signal(SIGALRM, catchalarm);
- alarm(timeout);
- if (!(pwd = getpwnam(name))) {
- syslog(LOG_WARNING, "No password entry for `root'\n");
- bb_error_msg_and_die("No password entry for `root'\n");
+ pwd = getpwuid(0);
+ if (!pwd) {
+ goto auth_error;
}
- pwent = *pwd;
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- spwd = NULL;
- if (pwd && ((strcmp(pwd->pw_passwd, "x") == 0)
- || (strcmp(pwd->pw_passwd, "*") == 0))) {
- endspent();
- spwd = getspnam(name);
- if (spwd) {
- pwent.pw_passwd = spwd->sp_pwdp;
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ {
+ /* getspnam_r may return 0 yet set result to NULL.
+ * At least glibc 2.4 does this. Be extra paranoid here. */
+ struct spwd *result = NULL;
+ int r = getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result);
+ if (r || !result) {
+ goto auth_error;
}
+ pwd->pw_passwd = result->sp_pwdp;
}
-#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
+#endif
+
while (1) {
- cp = getpass(SULOGIN_PROMPT);
+ char *encrypted;
+ int r;
+
+ /* cp points to a static buffer that is zeroed every time */
+ cp = bb_ask(STDIN_FILENO, timeout,
+ "Give root password for system maintenance\n"
+ "(or type Control-D for normal startup):");
+
if (!cp || !*cp) {
- puts("\n");
- fflush(stdout);
- syslog(LOG_INFO, "Normal startup\n");
- exit(EXIT_SUCCESS);
- } else {
- safe_strncpy(pass, cp, sizeof(pass));
- bzero(cp, strlen(cp));
+ bb_info_msg("Normal startup");
+ return 0;
}
- if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 0) {
+ encrypted = pw_encrypt(cp, pwd->pw_passwd, 1);
+ r = strcmp(encrypted, pwd->pw_passwd);
+ free(encrypted);
+ if (r == 0) {
break;
}
- time(&start);
- now = start;
- while (difftime(now, start) < FAIL_DELAY) {
- sleep(FAIL_DELAY);
- time(&now);
- }
- puts("Login incorrect");
- fflush(stdout);
- syslog(LOG_WARNING, "Incorrect root password\n");
+ bb_do_delay(FAIL_DELAY);
+ bb_error_msg("login incorrect");
}
- bzero(pass, strlen(pass));
- alarm(0);
- signal(SIGALRM, SIG_DFL);
- puts("Entering System Maintenance Mode\n");
- fflush(stdout);
- syslog(LOG_INFO, "System Maintenance Mode\n");
- run_shell(pwent.pw_shell, 1, 0, 0);
- return (0);
+ memset(cp, 0, strlen(cp));
+// signal(SIGALRM, SIG_DFL);
+
+ bb_info_msg("System Maintenance Mode");
+
+ USE_SELINUX(renew_current_security_context());
+
+ shell = getenv("SUSHELL");
+ if (!shell)
+ shell = getenv("sushell");
+ if (!shell) {
+ shell = "/bin/sh";
+ if (pwd->pw_shell[0])
+ shell = pwd->pw_shell;
+ }
+ /* Exec login shell with no additional parameters. Never returns. */
+ run_shell(shell, 1, NULL, NULL);
+
+ auth_error:
+ bb_error_msg_and_die("no password entry for root");
}
diff --git a/release/src/router/busybox/loginutils/vlock.c b/release/src/router/busybox/loginutils/vlock.c
index 7abf120d..85f489c2 100644
--- a/release/src/router/busybox/loginutils/vlock.c
+++ b/release/src/router/busybox/loginutils/vlock.c
@@ -5,20 +5,7 @@
* Copyright (C) 2000 by spoon <spoon@ix.netcom.com>
* Written by spoon <spon@ix.netcom.com>
*
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
*/
/* Shoutz to Michael K. Johnson <johnsonm@redhat.com>, author of the
@@ -28,149 +15,66 @@
/* Fixed by Erik Andersen to do passwords the tinylogin way...
* It now works with md5, sha1, etc passwords. */
-#include <stdio.h>
-#include <stdlib.h>
#include <sys/vt.h>
-#include <signal.h>
-#include <string.h>
-#include <unistd.h>
-#include <fcntl.h>
-#include <errno.h>
-#include <sys/ioctl.h>
-#include <termios.h>
-
-#include "busybox.h"
-
-static struct passwd *pw;
-static struct vt_mode ovtm;
-static struct termios oterm;
-static int vfd;
-static int o_lock_all = 0;
-
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
-static struct spwd *spw;
-
-/* getspuid - get a shadow entry by uid */
-struct spwd *getspuid(uid_t uid)
-{
- struct spwd *sp;
- struct passwd *mypw;
-
- if ((mypw = getpwuid(getuid())) == NULL) {
- return (NULL);
- }
- setspent();
- while ((sp = getspent()) != NULL) {
- if (strcmp(mypw->pw_name, sp->sp_namp) == 0)
- break;
- }
- endspent();
- return (sp);
-}
-#endif
+#include "libbb.h"
-static void release_vt(int signo)
+static void release_vt(int signo UNUSED_PARAM)
{
- if (!o_lock_all)
- ioctl(vfd, VT_RELDISP, 1);
- else
- ioctl(vfd, VT_RELDISP, 0);
+ /* If -a, param is 0, which means:
+ * "no, kernel, we don't allow console switch away from us!" */
+ ioctl(STDIN_FILENO, VT_RELDISP, (unsigned long) !option_mask32);
}
-static void acquire_vt(int signo)
+static void acquire_vt(int signo UNUSED_PARAM)
{
- ioctl(vfd, VT_RELDISP, VT_ACKACQ);
+ /* ACK to kernel that switch to console is successful */
+ ioctl(STDIN_FILENO, VT_RELDISP, VT_ACKACQ);
}
-static void restore_terminal(void)
+int vlock_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
+int vlock_main(int argc UNUSED_PARAM, char **argv)
{
- ioctl(vfd, VT_SETMODE, &ovtm);
- tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
-}
-
-extern int vlock_main(int argc, char **argv)
-{
- sigset_t sig;
- struct sigaction sa;
struct vt_mode vtm;
- int times = 0;
struct termios term;
-
- if (argc > 2) {
- bb_show_usage();
- }
-
- if (argc == 2) {
- if (strncmp(argv[1], "-a", 2)) {
- bb_show_usage();
- } else {
- o_lock_all = 1;
- }
- }
-
- if ((pw = getpwuid(getuid())) == NULL) {
- bb_error_msg_and_die("no password for uid %d\n", getuid());
- }
-#ifdef CONFIG_FEATURE_SHADOWPASSWDS
- if ((strcmp(pw->pw_passwd, "x") == 0)
- || (strcmp(pw->pw_passwd, "*") == 0)) {
-
- if ((spw = getspuid(getuid())) == NULL) {
- bb_error_msg_and_die("could not read shadow password for uid %d: %s\n",
- getuid(), strerror(errno));
- }
- if (spw->sp_pwdp) {
- pw->pw_passwd = spw->sp_pwdp;
- }
- }
-#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
- if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') {
- bb_error_msg_and_die("Account disabled for uid %d\n", getuid());
- }
-
- /* we no longer need root privs */
- setuid(getuid());
- setgid(getgid());
-
- if ((vfd = open("/dev/tty", O_RDWR)) < 0) {
- bb_error_msg_and_die("/dev/tty");
- };
-
- if (ioctl(vfd, VT_GETMODE, &vtm) < 0) {
- bb_error_msg_and_die("/dev/tty");
- };
-
- /* mask a bunch of signals */
- sigprocmask(SIG_SETMASK, NULL, &sig);
- sigdelset(&sig, SIGUSR1);
- sigdelset(&sig, SIGUSR2);
- sigaddset(&sig, SIGTSTP);
- sigaddset(&sig, SIGTTIN);
- sigaddset(&sig, SIGTTOU);
- sigaddset(&sig, SIGHUP);
- sigaddset(&sig, SIGCHLD);
- sigaddset(&sig, SIGQUIT);
- sigaddset(&sig, SIGINT);
-
- sigemptyset(&(sa.sa_mask));
- sa.sa_flags = SA_RESTART;
- sa.sa_handler = release_vt;
- sigaction(SIGUSR1, &sa, NULL);
- sa.sa_handler = acquire_vt;
- sigaction(SIGUSR2, &sa, NULL);
-
- /* need to handle some signals so that we don't get killed by them */
- sa.sa_handler = SIG_IGN;
- sigaction(SIGHUP, &sa, NULL);
- sigaction(SIGQUIT, &sa, NULL);
- sigaction(SIGINT, &sa, NULL);
- sigaction(SIGTSTP, &sa, NULL);
-
+ struct termios oterm;
+ struct vt_mode ovtm;
+ struct passwd *pw;
+
+ pw = xgetpwuid(getuid());
+ opt_complementary = "=0"; /* no params! */
+ getopt32(argv, "a");
+
+ /* Ignore some signals so that we don't get killed by them */
+ bb_signals(0
+ + (1 << SIGTSTP)
+ + (1 << SIGTTIN)
+ + (1 << SIGTTOU)
+ + (1 << SIGHUP )
+ + (1 << SIGCHLD) /* paranoia :) */
+ + (1 << SIGQUIT)
+ + (1 << SIGINT )
+ , SIG_IGN);
+
+ /* We will use SIGUSRx for console switch control: */
+ /* 1: set handlers */
+ signal_SA_RESTART_empty_mask(SIGUSR1, release_vt);
+ signal_SA_RESTART_empty_mask(SIGUSR2, acquire_vt);
+ /* 2: unmask them */
+ sig_unblock(SIGUSR1);
+ sig_unblock(SIGUSR2);
+
+ /* Revert stdin/out to our controlling tty
+ * (or die if we have none) */
+ xmove_fd(xopen(CURRENT_TTY, O_RDWR), STDIN_FILENO);
+ xdup2(STDIN_FILENO, STDOUT_FILENO);
+
+ xioctl(STDIN_FILENO, VT_GETMODE, &vtm);
ovtm = vtm;
+ /* "console switches are controlled by us, not kernel!" */
vtm.mode = VT_PROCESS;
vtm.relsig = SIGUSR1;
vtm.acqsig = SIGUSR2;
- ioctl(vfd, VT_SETMODE, &vtm);
+ ioctl(STDIN_FILENO, VT_SETMODE, &vtm);
tcgetattr(STDIN_FILENO, &oterm);
term = oterm;
@@ -178,56 +82,20 @@ extern int vlock_main(int argc, char **argv)
term.c_iflag |= IGNBRK;
term.c_lflag &= ~ISIG;
term.c_lflag &= ~(ECHO | ECHOCTL);
- tcsetattr(STDIN_FILENO, TCSANOW, &term);
+ tcsetattr_stdin_TCSANOW(&term);
do {
- char *pass, *crypt_pass;
- char prompt[100];
-
- if (o_lock_all) {
- printf("All Virtual Consoles locked.\n");
- } else {
- printf("This Virtual Console locked.\n");
- }
- fflush(stdout);
-
- snprintf(prompt, 100, "%s's password: ", pw->pw_name);
-
- if ((pass = getpass(prompt)) == NULL) {
- perror("getpass");
- restore_terminal();
- exit(1);
- }
-
- crypt_pass = pw_encrypt(pass, pw->pw_passwd);
- if (strncmp(crypt_pass, pw->pw_passwd, sizeof(crypt_pass)) == 0) {
- memset(pass, 0, strlen(pass));
- memset(crypt_pass, 0, strlen(crypt_pass));
- restore_terminal();
- return 0;
- }
- memset(pass, 0, strlen(pass));
- memset(crypt_pass, 0, strlen(crypt_pass));
-
- if (isatty(STDIN_FILENO) == 0) {
- perror("isatty");
- restore_terminal();
- exit(1);
- }
-
- sleep(++times);
- printf("Password incorrect.\n");
- if (times >= 3) {
- sleep(15);
- times = 2;
+ printf("Virtual console%s locked by %s.\n",
+ option_mask32 /*o_lock_all*/ ? "s" : "",
+ pw->pw_name);
+ if (correct_password(pw)) {
+ break;
}
+ bb_do_delay(FAIL_DELAY);
+ puts("Password incorrect");
} while (1);
-}
-/*
-Local Variables:
-c-file-style: "linux"
-c-basic-offset: 4
-tab-width: 4
-End:
-*/
+ ioctl(STDIN_FILENO, VT_SETMODE, &ovtm);
+ tcsetattr_stdin_TCSANOW(&oterm);
+ fflush_stdout_and_exit(EXIT_SUCCESS);
+}