From 4aca87515a5083ae0e31ce3177189fd43b6d05ac Mon Sep 17 00:00:00 2001 From: Andreas Baumann Date: Sat, 3 Jan 2015 13:58:15 +0100 Subject: patch to Vanilla Tomato 1.28 --- release/src/router/busybox/loginutils/Config.in | 296 +++-- release/src/router/busybox/loginutils/Kbuild | 19 + release/src/router/busybox/loginutils/Makefile | 30 - release/src/router/busybox/loginutils/Makefile.in | 49 - release/src/router/busybox/loginutils/addgroup.c | 220 ++-- release/src/router/busybox/loginutils/adduser.c | 374 ++---- release/src/router/busybox/loginutils/chpasswd.c | 71 ++ release/src/router/busybox/loginutils/cryptpw.c | 117 ++ release/src/router/busybox/loginutils/delgroup.c | 62 - release/src/router/busybox/loginutils/delline.c | 113 -- release/src/router/busybox/loginutils/deluser.c | 92 +- release/src/router/busybox/loginutils/getty.c | 1278 +++++++++------------ release/src/router/busybox/loginutils/login.c | 871 +++++++------- release/src/router/busybox/loginutils/passwd.c | 562 +++------ release/src/router/busybox/loginutils/su.c | 214 ++-- release/src/router/busybox/loginutils/sulogin.c | 253 ++-- release/src/router/busybox/loginutils/vlock.c | 248 +--- 17 files changed, 2054 insertions(+), 2815 deletions(-) mode change 100755 => 100644 release/src/router/busybox/loginutils/Config.in create mode 100644 release/src/router/busybox/loginutils/Kbuild delete mode 100644 release/src/router/busybox/loginutils/Makefile delete mode 100755 release/src/router/busybox/loginutils/Makefile.in create mode 100644 release/src/router/busybox/loginutils/chpasswd.c create mode 100644 release/src/router/busybox/loginutils/cryptpw.c delete mode 100644 release/src/router/busybox/loginutils/delgroup.c delete mode 100644 release/src/router/busybox/loginutils/delline.c (limited to 'release/src/router/busybox/loginutils') diff --git a/release/src/router/busybox/loginutils/Config.in b/release/src/router/busybox/loginutils/Config.in old mode 100755 new mode 100644 index 9eb771f0..ddd0c801 --- 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 +# +# 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 -# -# 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 -# -# 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 + * Copyright (C) 2007 by Tito Ragusa * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 * - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 + * 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 + * 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 - * - * 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 -#include -#include -#include -#include -#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 - * - * 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 -#include -#include -#include -#include -#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 + * Copyright (C) 2007 by Tito Ragusa * - * 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 -#include -#include -#include -#include -#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 - 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 - 12/28/95 - - 1999-02-22 Arkadiusz Mi¶kiewicz - - added Native Language Support - - 1999-05-05 Thorsten Kranzkowski - - enable hardware flow control before displaying /etc/issue - -*/ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "busybox.h" - -#define _PATH_LOGIN "/bin/login" - - /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */ -#ifdef CONFIG_SYSLOGD -#include -#define USE_SYSLOG -#include -#endif - + * Ported to Linux by Peter Orbaek + * 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 - 12/28/95 + * + * 1999-02-22 Arkadiusz Mickiewicz + * - added Native Language Support + * + * 1999-05-05 Thorsten Kranzkowski + * - 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 -#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 /* 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 -#include -#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: - * + * * - * + * * 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 -#include -#include -#include -#include +/* + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" #include -#include -#include #include #include -#include -#include -#include -#include -#include - -#include "busybox.h" -#ifdef CONFIG_SELINUX -#include -#include -#include -#include -#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 /* for is_selinux_enabled() */ +#include /* for get_default_context() */ +#include /* 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 . 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 +#include +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; ipw_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 " 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 -#include -#include -#include -#include -#include -#include -#include +/* + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ +#include "libbb.h" #include -#include -#include -#include -#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 -#include -#include -#include -#include +#include "libbb.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#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 -#include -#include -#include -#include +/* + * Mini sulogin implementation for busybox + * + * Licensed under GPLv2 or later, see file LICENSE in this tarball for details. + */ + +#include "libbb.h" #include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#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 * Written by spoon * - * 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 , 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 -#include #include -#include -#include -#include -#include -#include -#include -#include - -#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); +} -- cgit v1.2.3-54-g00ecf