diff options
author | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 12:04:58 +0100 |
---|---|---|
committer | Andreas Baumann <mail@andreasbaumann.cc> | 2015-01-03 12:04:58 +0100 |
commit | 008d0be72b2f160382c6e880765e96b64a050c65 (patch) | |
tree | 36f48a98a3815a408e2ce1693dd182af90f80305 /release/src/router/busybox/loginutils | |
parent | 611becfb8726c60cb060368541ad98191d4532f5 (diff) | |
download | tomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.gz tomato-008d0be72b2f160382c6e880765e96b64a050c65.tar.bz2 |
imported original firmware WRT54GL_v4.30.11_11_US
Diffstat (limited to 'release/src/router/busybox/loginutils')
-rwxr-xr-x | release/src/router/busybox/loginutils/Config.in | 136 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/Makefile | 30 | ||||
-rwxr-xr-x | release/src/router/busybox/loginutils/Makefile.in | 49 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/addgroup.c | 171 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/adduser.c | 320 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/delgroup.c | 62 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/delline.c | 113 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/deluser.c | 68 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/getty.c | 1019 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/login.c | 481 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/passwd.c | 406 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/su.c | 166 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/sulogin.c | 184 | ||||
-rw-r--r-- | release/src/router/busybox/loginutils/vlock.c | 233 |
14 files changed, 3438 insertions, 0 deletions
diff --git a/release/src/router/busybox/loginutils/Config.in b/release/src/router/busybox/loginutils/Config.in new file mode 100755 index 00000000..9eb771f0 --- /dev/null +++ b/release/src/router/busybox/loginutils/Config.in @@ -0,0 +1,136 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +menu "Login/Password Management Utilities" + +config 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. + + 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 enable this option, it will add about 1.5k to busybox. + + +config CONFIG_ADDGROUP + bool "addgroup" + default n + help + Utility for creating a new group account. + +config CONFIG_DELGROUP + bool "delgroup" + default n + help + Utility for deleting a group account. + +config CONFIG_ADDUSER + bool "adduser" + default n + help + Utility for creating a new user account. + +config CONFIG_DELUSER + bool "deluser" + default n + help + Utility for deleting a user account. + +config CONFIG_GETTY + bool "getty" + default n + help + Getty lets you log in on a tty, it is normally invoked by init. + +config CONFIG_LOGIN + bool "login" + default n + help + Login is used when signing onto a system. + +config CONFIG_FEATURE_SECURETTY + bool " Support for /etc/securetty" + default y + depends on CONFIG_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. + +config CONFIG_PASSWD + bool "passwd" + default n + 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 + may change the password for the group. + +config CONFIG_SU + bool "su" + 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. + +config CONFIG_SULOGIN + bool "sulogin" + default n + help + Sulogin is invoked when the system goes into single user + mode (this is done through an entry in inittab). + +config CONFIG_VLOCK + bool "vlock" + default n + help + Build the "vlock" applet, that allows you to lock (virtual) terminals. + +comment "Common options for adduser, deluser, login, su" + depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU + +config CONFIG_FEATURE_SHADOWPASSWDS + bool "Support for shadow passwords" + default n + depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU + 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 CONFIG_USE_BB_SHADOW + bool " Use busybox shadow password functions" + default n + depends on CONFIG_USE_BB_PWD_GRP && CONFIG_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. + +endmenu + diff --git a/release/src/router/busybox/loginutils/Makefile b/release/src/router/busybox/loginutils/Makefile new file mode 100644 index 00000000..3359d563 --- /dev/null +++ b/release/src/router/busybox/loginutils/Makefile @@ -0,0 +1,30 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +TOPDIR:= ../ +LOGINUTILS_DIR:=./ +include $(TOPDIR).config +include $(TOPDIR)Rules.mak +include Makefile.in +all: $(libraries-y) +-include $(TOPDIR).depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/release/src/router/busybox/loginutils/Makefile.in b/release/src/router/busybox/loginutils/Makefile.in new file mode 100755 index 00000000..31ce8bd9 --- /dev/null +++ b/release/src/router/busybox/loginutils/Makefile.in @@ -0,0 +1,49 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2003 by Erik Andersen <andersen@codepoet.org> +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +LOGINUTILS_AR:=loginutils.a +ifndef LOGINUTILS_DIR +LOGINUTILS_DIR:=$(TOPDIR)loginutils/ +endif + +LOGINUTILS-y:= +LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o +LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o +LOGINUTILS-$(CONFIG_GETTY) += getty.o +LOGINUTILS-$(CONFIG_LOGIN) += login.o +LOGINUTILS-$(CONFIG_PASSWD) += passwd.o +LOGINUTILS-$(CONFIG_SU) += su.o +LOGINUTILS-$(CONFIG_SULOGIN) += sulogin.o +LOGINUTILS-$(CONFIG_VLOCK) += vlock.o +LOGINUTILS-$(CONFIG_DELUSER) += deluser.o +LOGINUTILS-$(CONFIG_DELGROUP) += delgroup.o + +libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR) + +needcrypt-y:= +needcrypt-$(CONFIG_LOGIN) := y +needcrypt-$(CONFIG_SU) := y + +ifeq ($(needcrypt-y),y) + LIBRARIES += -lcrypt +endif + +$(LOGINUTILS_DIR)$(LOGINUTILS_AR): $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y)) + $(AR) -ro $@ $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y)) + diff --git a/release/src/router/busybox/loginutils/addgroup.c b/release/src/router/busybox/loginutils/addgroup.c new file mode 100644 index 00000000..af1cd7a8 --- /dev/null +++ b/release/src/router/busybox/loginutils/addgroup.c @@ -0,0 +1,171 @@ +/* vi: set sw=4 ts=4: */ +/* + * addgroup - add users to /etc/passwd and /etc/shadow + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include "busybox.h" +#include "pwd_.h" +#include "grp_.h" + + +/* structs __________________________ */ + +/* data _____________________________ */ + +/* defaults : should this be in an external file? */ +static const char default_passwd[] = "x"; + + +/* make sure gr_name isn't taken, make sure gid is kosher + * return 1 on failure */ +static int group_study(const char *filename, struct group *g) +{ + FILE *etc_group; + gid_t desired; + + struct group *grp; + const int max = 65000; + + etc_group = bb_xfopen(filename, "r"); + + /* 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); + } + if ((desired) && grp->gr_gid == desired) { + bb_error_msg_and_die("%d: gid has already been allocated\n", + desired); + } + if ((grp->gr_gid > g->gr_gid) && (grp->gr_gid < max)) { + g->gr_gid = grp->gr_gid; + } + } + fclose(etc_group); + + /* gid */ + if (desired) { + g->gr_gid = desired; + } else { + g->gr_gid++; + } + /* return 1; */ + return 0; +} + +/* append a new user to the passwd file */ +static int addgroup(const char *filename, char *group, gid_t gid, const char *user) +{ + 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"; + + /* 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; + + /* 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); + } +#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) +{ + char *group; + char *user; + gid_t gid = 0; + + /* get remaining args */ + if(bb_getopt_ulflags(argc, argv, "g:", &group)) { + gid = strtol(group, NULL, 10); + } + + if (optind < argc) { + group = argv[optind]; + optind++; + } else { + bb_show_usage(); + } + + if (optind < argc) { + user = argv[optind]; + optind++; + } else { + user = ""; + } + if_i_am_not_root(); + + /* werk */ + return addgroup(bb_path_group_file, group, gid, user); +} diff --git a/release/src/router/busybox/loginutils/adduser.c b/release/src/router/busybox/loginutils/adduser.c new file mode 100644 index 00000000..41dc9f01 --- /dev/null +++ b/release/src/router/busybox/loginutils/adduser.c @@ -0,0 +1,320 @@ +/* vi: set sw=4 ts=4: */ +/* + * adduser - add users to /etc/passwd and /etc/shadow + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#ifndef _GNU_SOURCE +#define _GNU_SOURCE +#endif +#include <errno.h> +#include <fcntl.h> +#include <stdarg.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <unistd.h> +#include <getopt.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/types.h> +#include "busybox.h" + + + +/* structs __________________________ */ + +typedef struct { + uid_t u; + gid_t g; +} Id; + +/* data _____________________________ */ + +/* defaults : should this be in an external file? */ +static const char default_passwd[] = "x"; +static const char default_gecos[] = "Linux User,,,"; +static const char default_home_prefix[] = "/home"; +static const char default_shell[] = "/bin/sh"; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS +/* shadow in use? */ +static int shadow_enabled = 0; +#endif + +/* remix */ +/* EDR recoded such that the uid may be passed in *p */ +static int passwd_study(const char *filename, struct passwd *p) +{ + struct passwd *pw; + FILE *passwd; + + const int min = 500; + const int max = 65000; + + passwd = bb_wfopen(filename, "r"); + if (!passwd) + return 4; + + /* 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 (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; + } + + /* EDR bounds check */ + if ((p->pw_uid > max) || (p->pw_uid < min)) + return 2; + + /* return 1; */ + return 0; +} + +static void addgroup_wrapper(const char *login, gid_t gid) +{ + char *cmd; + + bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login); + system(cmd); + free(cmd); +} + +static void passwd_wrapper(const char *login) __attribute__ ((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); + + /* 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; +} + + +/* 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) +#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 + * + * can be customized via command-line parameters. + * ________________________________________________________________________ */ +int adduser_main(int argc, 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; + + /* 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 (flags & SETPASS) { + setpass = 0; + } + if (flags & MAKEHOME) { + makehome = 0; + } + + /* got root? */ + if_i_am_not_root(); + + /* get login */ + if (optind >= argc) { + bb_error_msg_and_die( "no user specified"); + } + login = argv[optind]; + + /* create string for $HOME if not specified already */ + if (!home) { + home = concat_path_file(default_home_prefix, login); + } +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + /* is /etc/shadow in use? */ + shadow_enabled = (0 == access(bb_path_shadow_file, F_OK)); +#endif + + /* 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 (usegroup) { + /* Add user to a group that already exists */ + struct group *g; + + g = getgrnam(usegroup); + if (g == NULL) + bb_error_msg_and_die("group %s does not exist", usegroup); + + pw.pw_gid = g->gr_gid; + } + + /* grand finale */ + return adduser(bb_path_passwd_file, &pw, makehome, setpass); +} diff --git a/release/src/router/busybox/loginutils/delgroup.c b/release/src/router/busybox/loginutils/delgroup.c new file mode 100644 index 00000000..fe3dcb8d --- /dev/null +++ b/release/src/router/busybox/loginutils/delgroup.c @@ -0,0 +1,62 @@ +/* vi: set sw=4 ts=4: */ +/* + * deluser (remove lusers from the system ;) for TinyLogin + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "busybox.h" + + +#if ! defined CONFIG_DELUSER +#include "delline.c" +#else +extern int del_line_matching(const char *login, const char *filename); +#endif + +int delgroup_main(int argc, char **argv) +{ + /* int successful; */ + int failure; + + if (argc != 2) { + bb_show_usage(); + } else { + + failure = del_line_matching(argv[1], bb_path_group_file); +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + if (access(bb_path_gshadow_file, W_OK) == 0) { + /* EDR the |= works if the error is not 0, so he had it wrong */ + failure |= del_line_matching(argv[1], bb_path_gshadow_file); + } +#endif + if (failure) { + bb_error_msg_and_die("%s: Group could not be removed\n", argv[1]); + } + + } + return (EXIT_SUCCESS); +} + +/* $Id: delgroup.c,v 1.1.3.1 2004/12/29 07:07:45 honor Exp $ */ diff --git a/release/src/router/busybox/loginutils/delline.c b/release/src/router/busybox/loginutils/delline.c new file mode 100644 index 00000000..3f5f73ff --- /dev/null +++ b/release/src/router/busybox/loginutils/delline.c @@ -0,0 +1,113 @@ +/* vi: set sw=4 ts=4: */ +/* + * deluser (remove lusers from the system ;) for TinyLogin + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "busybox.h" + + + +/* where to start and stop deletion */ +typedef struct { + size_t start; + size_t stop; +} Bounds; + +/* An interesting side-effect of boundary()'s + * implementation is that the first user (typically root) + * cannot be removed. Let's call it a feature. */ +static inline Bounds boundary(const char *buffer, const char *login) +{ + char needle[256]; + char *start; + char *stop; + Bounds b; + + snprintf(needle, 256, "\n%s:", login); + needle[255] = 0; + start = strstr(buffer, needle); + if (!start) { + b.start = 0; + b.stop = 0; + return b; + } + start++; + + stop = index(start, '\n'); /* index is a BSD-ism */ + b.start = start - buffer; + b.stop = stop - buffer; + return b; +} + +/* grep -v ^login (except it only deletes the first match) */ +/* ...in fact, I think I'm going to simplify this later */ +int del_line_matching(const char *login, const char *filename) +{ + char *buffer; + FILE *passwd; + size_t len; + Bounds b; + struct stat statbuf; + + /* load into buffer */ + passwd = fopen(filename, "r"); + if (!passwd) { + return 1; + } + stat(filename, &statbuf); + len = statbuf.st_size; + buffer = (char *) malloc(len * sizeof(char)); + + if (!buffer) { + fclose(passwd); + return 1; + } + fread(buffer, len, sizeof(char), passwd); + + fclose(passwd); + + /* find the user to remove */ + b = boundary(buffer, login); + if (b.stop == 0) { + free(buffer); + return 1; + } + + /* write the file w/o the user */ + passwd = fopen(filename, "w"); + if (!passwd) { + return 1; + } + fwrite(buffer, (b.start - 1), sizeof(char), passwd); + fwrite(&buffer[b.stop], (len - b.stop), sizeof(char), passwd); + + fclose(passwd); + + return 0; +} + + +/* $Id: delline.c,v 1.1.3.1 2004/12/29 07:07:45 honor Exp $ */ diff --git a/release/src/router/busybox/loginutils/deluser.c b/release/src/router/busybox/loginutils/deluser.c new file mode 100644 index 00000000..f84076da --- /dev/null +++ b/release/src/router/busybox/loginutils/deluser.c @@ -0,0 +1,68 @@ +/* vi: set sw=4 ts=4: */ +/* + * deluser (remove lusers from the system ;) for TinyLogin + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include <sys/stat.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include "busybox.h" + + +#include "delline.c" + +static const char deluser_format[]="%s: User could not be removed from %s"; + +int deluser_main(int argc, char **argv) +{ + /* int successful; */ + int failure; + + if (argc != 2) { + 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); + } + + } + 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 new file mode 100644 index 00000000..4219ff82 --- /dev/null +++ b/release/src/router/busybox/loginutils/getty.c @@ -0,0 +1,1019 @@ +/* vi: set sw=4 ts=4: */ +/* agetty.c - another getty program for Linux. By W. Z. Venema 1989 + Ported to Linux by Peter Orbaek <poe@daimi.aau.dk> + This program is freely distributable. The entire man-page used to + be here. Now read the real man-page agetty.8 instead. + + -f option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95 + + 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org> + - added Native Language Support + + 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net> + - enable hardware flow control before displaying /etc/issue + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <sys/stat.h> +#include <sys/signal.h> +#include <fcntl.h> +#include <stdarg.h> +#include <ctype.h> +#include <utmp.h> +#include <getopt.h> +#include <termios.h> +#include "busybox.h" + +#define _PATH_LOGIN "/bin/login" + + /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */ +#ifdef CONFIG_SYSLOGD +#include <sys/param.h> +#define USE_SYSLOG +#include <syslog.h> +#endif + + + /* + * 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 */ +#define SYSV_STYLE /* select System V style getty */ +#ifdef CONFIG_FEATURE_U_W_TMP +extern void updwtmp(const char *filename, const struct utmp *ut); +#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 */ +#include <sys/utsname.h> +#include <time.h> +#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 */ + +/* 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 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 + + /* + * 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 + + /* + * When multiple baud rates are specified on the command line, the first one + * we will try is the first one specified. + */ + +#define FIRST_SPEED 0 + +/* 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 */ +}; + +#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 */ +}; + +/* 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}, +#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 + +/* The following is used for understandable diagnostics. */ + +/* Fake hostname for ut_host specified on command line. */ +static char *fakehost = NULL; + +/* ... */ +#ifdef DEBUGGING +#define debug(s) fprintf(dbf,s); fflush(dbf) +#define DEBUGTERM "/dev/ttyp0" +FILE *dbf; +#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); +#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) +{ + 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"); +} + +/* parse_speeds - parse alternate baud rates */ + +static void parse_speeds(struct options *op, char *arg) +{ + char *cp; + + 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); + if (op->numspeed > MAX_SPEED) + error("too many alternate speeds"); + } + debug("exiting parsespeeds\n"); +} + +#ifdef SYSV_STYLE +#ifdef CONFIG_FEATURE_U_W_TMP + +/* update_utmp - update our utmp entry */ +static void update_utmp(char *line) +{ + 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 */ + ; + + 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)); + } + /*endutent(); */ + + 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; + + pututline(&ut); + endutent(); + + { + updwtmp(_PATH_WTMP, &ut); + } +} + +#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) +{ + /* 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; + + /* Sanity checks... */ + + 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); + + /* Open the tty as standard input. */ + + (void) close(0); + errno = 0; /* ignore close(2) errors */ + + debug("open(2)\n"); + if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0) + error("/dev/%s: cannot open as standard input: %m", tty); + + } 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); + } + + /* 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) +{ + + /* + * Initial termio 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. + */ +#ifdef __linux__ + /* flush input and output queues, important for modems! */ + (void) ioctl(0, TCFLSH, TCIOFLUSH); +#endif + + tp->c_cflag = CS8 | HUPCL | CREAD | speed; + if (op->flags & F_LOCAL) { + tp->c_cflag |= CLOCAL; + } + + tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0; + tp->c_cc[VMIN] = 1; + tp->c_cc[VTIME] = 0; + + /* Optionally enable hardware flow control */ + +#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); + + debug("term_io 2\n"); +} + +/* auto_baud - extract baud rate from modem status message */ +static void auto_baud(struct termio *tp) +{ + int speed; + int vmin; + unsigned iflag; + char buf[BUFSIZ]; + char *bp; + int nread; + + /* + * This works only if the modem produces its status code AFTER raising + * the DCD line, and if the computer is fast enough to set the proper + * baud rate before the message has gone by. We expect a message of the + * following format: + * + * <junk><number><junk> + * + * The number is interpreted as the baud rate of the incoming call. If the + * modem does not tell us the baud rate within one second, we will keep + * using the current baud rate. It is advisable to enable BREAK + * processing (comma-separated list of baud rates) if the processing of + * modem status messages is enabled. + */ + + /* + * Use 7-bit characters, don't block if input queue is empty. Errors will + * be dealt with lateron. + */ + + iflag = tp->c_iflag; + 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); + + /* + * 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) { + buf[nread] = '\0'; + for (bp = buf; bp < buf + nread; bp++) { + if (isascii(*bp) && isdigit(*bp)) { + if ((speed = bcode(bp))) { + tp->c_cflag &= ~CBAUD; + tp->c_cflag |= speed; + } + break; + } + } + } + /* Restore terminal settings. Errors will be dealt with lateron. */ + + tp->c_iflag = iflag; + tp->c_cc[VMIN] = vmin; + (void) ioctl(0, TCSETA, tp); +} + +/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ +static void do_prompt(struct options *op, struct termio *tp) +{ +#ifdef ISSUE /* optional: show /etc/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) +{ + 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); +} + +/* 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) +{ + 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 */ + }; + + /* Initialize kill, erase, parity etc. (also after switching speeds). */ + + *cp = init_chardata; + + /* Flush pending input (esp. after parsing or switching the baud rate). */ + + (void) sleep(1); + (void) ioctl(0, TCFLSH, TCIFLUSH); + + /* Prompt for and read a login name. */ + + for (*logname = 0; *logname == 0; /* void */ ) { + + /* Write issue file and prompt, with "parity" bit == 0. */ + + do_prompt(op, tp); + + /* Read name, watch for break, parity, erase, kill, end-of-line. */ + + for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) { + + /* Do not report trivial EINTR/EIO errors. */ + + if (read(0, &c, 1) < 1) { + if (errno == EINTR || errno == EIO) + exit(0); + error("%s: read: %m", op->tty); + } + /* Do BREAK handling elsewhere. */ + + if ((c == 0) && op->numspeed > 1) + /* return (0); */ + 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); + } + /* Do erase, kill and end-of-line processing. */ + + switch (ascval) { + case CR: + case NL: + *bp = 0; /* terminate logname */ + cp->eol = ascval; /* set end-of-line char */ + break; + case BS: + case DEL: + case '#': + cp->erase = ascval; /* set erase character */ + if (bp > logname) { + (void) write(1, erase[cp->parity], 3); + bp--; + } + break; + case CTL('U'): + case '@': + cp->kill = ascval; /* set kill character */ + while (bp > logname) { + (void) write(1, erase[cp->parity], 3); + bp--; + } + break; + case CTL('D'): + exit(0); + default: + if (!isascii(ascval) || !isprint(ascval)) { + /* ignore garbage characters */ ; + } else if (bp - logname >= sizeof(logname) - 1) { + error("%s: input overrun", op->tty); + } else { + (void) write(1, &c, 1); /* echo the character */ + *bp++ = ascval; /* and store it */ + } + break; + } + } + } + /* Handle names with upper case and no lower case. */ + + if ((cp->capslock = caps_lock(logname))) { + for (bp = logname; *bp; bp++) + if (isupper(*bp)) + *bp = tolower(*bp); /* map name to lower case */ + } + return (logname); +} + +/* termio_final - set the final tty mode bits */ +static void termio_final(struct options *op, struct termio *tp, struct chardata *cp) +{ + /* General terminal-independent stuff. */ + + 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[VEOL] = DEF_EOL; + 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_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 */ + break; + case 1: /* odd parity */ + tp->c_cflag |= PARODD; + /* FALLTHROUGH */ + case 2: /* even parity */ + tp->c_cflag |= PARENB; + tp->c_iflag |= INPCK | ISTRIP; + /* FALLTHROUGH */ + case (1 | 2): /* no parity bit */ + tp->c_cflag &= ~CSIZE; + tp->c_cflag |= CS7; + break; + } + /* Account for upper case without lower case. */ + + if (cp->capslock) { + tp->c_iflag |= IUCLC; + tp->c_lflag |= XCASE; + tp->c_oflag |= OLCUC; + } + /* Optionally enable hardware flow control */ + +#ifdef CRTSCTS + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; +#endif + + /* Finally, make the new settings effective */ + + if (ioctl(0, TCSETA, tp) < 0) + error("%s: ioctl: TCSETA: %m", op->tty); +} + +/* caps_lock - string contains upper case without lower case */ +/* returns 1 if true, 0 if false */ +static int caps_lock(const char *s) +{ + int capslock; + + for (capslock = 0; *s; s++) { + if (islower(*s)) + return (0); + if (capslock == 0) + capslock = isupper(*s); + } + return (capslock); +} + +/* bcode - convert speed string to speed code; return 0 on failure */ +static int bcode(const char *s) +{ +#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; + + if ((r = bb_value_to_baud(atol(s))) > 0) { + return r; + } + return 0; +#endif +} + +/* error - report errors to console or syslog; only understands %s and %m */ + +#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2) + +/* + * output error messages + */ +static void error(const char *fmt, ...) +{ + va_list va_alist; + char buf[256], *bp; + +#ifndef USE_SYSLOG + int fd; +#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); +#endif + + va_start(va_alist, fmt); + vsnprintf(bp, 256 - strlen(buf), fmt, va_alist); + buf[255] = 0; + va_end(va_alist); + +#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); + } +#endif + (void) sleep((unsigned) 10); /* be kind to init(8) */ + exit(1); +} diff --git a/release/src/router/busybox/loginutils/login.c b/release/src/router/busybox/loginutils/login.c new file mode 100644 index 00000000..c2bada25 --- /dev/null +++ b/release/src/router/busybox/loginutils/login.c @@ -0,0 +1,481 @@ +/* vi: set sw=4 ts=4: */ +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <termios.h> +#include <unistd.h> +#include <utmp.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <ctype.h> +#include <time.h> + +#include "busybox.h" +#ifdef CONFIG_SELINUX +#include <flask_util.h> +#include <get_sid_list.h> +#include <proc_secure.h> +#include <fs_secure.h> +#endif + +#ifdef CONFIG_FEATURE_U_W_TMP +// import from utmp.c +static void checkutmp(int picky); +static void setutmp(const char *name, const char *line); +/* Stuff global to this file */ +struct utmp utent; +#endif + +// login defines +#define TIMEOUT 60 +#define EMPTY_USERNAME_COUNT 10 +#define USERNAME_SIZE 32 + + +static int check_nologin ( int amroot ); + +#if defined CONFIG_FEATURE_SECURETTY +static int check_tty ( const char *tty ); + +#else +static inline int check_tty ( const char *tty ) { return 1; } + +#endif + +static int is_my_tty ( const char *tty ); +static int login_prompt ( char *buf_name ); +static void motd ( void ); + + +static void alarm_handler ( int sig ) +{ + fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT ); + exit ( EXIT_SUCCESS ); +} + + +extern int login_main(int argc, char **argv) +{ + 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; +#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; + break; + default: + bb_show_usage( ); + } + } + + 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 )); +#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; + + { // delay next try + time_t start, now; + + time ( &start ); + now = start; + while ( difftime ( now, start ) < FAIL_DELAY) { + sleep ( FAIL_DELAY ); + time ( &now ); + } + } + + 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; + } + } + + 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; + } + } + 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; +} + + + +static int login_prompt ( char *buf_name ) +{ + char buf [1024]; + char *sp, *ep; + int i; + + for(i=0; i<EMPTY_USERNAME_COUNT; i++) { + print_login_prompt(); + + if ( !fgets ( buf, sizeof( buf ) - 1, stdin )) + return 0; + + if ( !strchr ( buf, '\n' )) + return 0; + + for ( sp = buf; isspace ( *sp ); sp++ ) { } + for ( ep = sp; isgraph ( *ep ); ep++ ) { } + + *ep = 0; + safe_strncpy(buf_name, sp, USERNAME_SIZE); + if(buf_name[0]) + return 1; + } + return 0; +} + + +static int check_nologin ( int amroot ) +{ + 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" ); + } + if ( !amroot ) + return 1; + + puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" ); + } + return 0; +} + +#ifdef CONFIG_FEATURE_SECURETTY + +static int check_tty ( const char *tty ) +{ + 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; + } + /* A missing securetty file is not an error. */ + return 1; +} + +#endif + +/* returns 1 if true */ +static int is_my_tty ( const char *tty ) +{ + 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; +} + + +static void motd ( ) +{ + FILE *fp; + register int c; + + if (( fp = fopen ( bb_path_motd_file, "r" ))) { + while (( c = getc ( fp )) != EOF ) + putchar ( c ); + fclose ( fp ); + } +} + + +#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. + */ + +static void checkutmp(int picky) +{ + char *line; + struct utmp *ut; + pid_t pid = getpid(); + + setutent(); + + /* 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 there is one, just use it, otherwise create a new one. */ + if (ut) { + utent = *ut; + } else { + if (picky) { + puts(NO_UTENT); + exit(1); + } + line = ttyname(0); + if (!line) { + puts(NO_TTY); + exit(1); + } + 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. + */ + +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); +} +#endif /* CONFIG_FEATURE_U_W_TMP */ diff --git a/release/src/router/busybox/loginutils/passwd.c b/release/src/router/busybox/loginutils/passwd.c new file mode 100644 index 00000000..e8577066 --- /dev/null +++ b/release/src/router/busybox/loginutils/passwd.c @@ -0,0 +1,406 @@ +/* vi: set sw=4 ts=4: */ +#include <fcntl.h> +#include <stdio.h> +#include <string.h> +#include <signal.h> +#include <sys/stat.h> +#include <sys/types.h> +#include <unistd.h> +#include <utime.h> +#include <syslog.h> +#include <time.h> +#include <sys/resource.h> +#include <errno.h> + +#include "busybox.h" + +static char crypt_passwd[128]; + +static int create_backup(const char *backup, FILE * fp); +static int new_password(const struct passwd *pw, int amroot, int algo); +static void set_filesize_limit(int blocks); + + +int get_algo(char *a) +{ + int x = 1; /* standard: MD5 */ + + if (strcasecmp(a, "des") == 0) + x = 0; + return x; +} + + +extern int update_passwd(const struct passwd *pw, char *crypt_pw) +{ + 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; + } + 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; + } +} + + +extern int passwd_main(int argc, char **argv) +{ + int amroot; + char *cp; + char *np; + char *name; + 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)) { + 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); +} + + + +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; + } + 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: "))) + { + 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"); + } else { + /* return -1; */ + return 1; + } + } + 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; +} + +static void set_filesize_limit(int blocks) +{ + struct rlimit rlimit_fsize; + + rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks; + setrlimit(RLIMIT_FSIZE, &rlimit_fsize); +} diff --git a/release/src/router/busybox/loginutils/su.c b/release/src/router/busybox/loginutils/su.c new file mode 100644 index 00000000..85f5cbe7 --- /dev/null +++ b/release/src/router/busybox/loginutils/su.c @@ -0,0 +1,166 @@ +/* vi: set sw=4 ts=4: */ + +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <termios.h> +#include <unistd.h> +#include <utmp.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <ctype.h> +#include <time.h> + +#include "busybox.h" + + + +/* 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 ) +{ + 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 : "" ); + } + + tty = ttyname ( 2 ); + + openlog ( "su", 0, LOG_AUTH ); + syslog ( LOG_NOTICE, "%s%s on %s", successful ? "" : "FAILED SU ", old_user, tty ? tty : "none" ); +} +#endif + + + +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 (( optind < argc ) && ( argv [optind][0] == '-' ) && ( argv [optind][1] == 0 )) { + opt_loginshell = 1; + ++optind; + } + + /* 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 ( !opt_shell && opt_preserve ) + opt_shell = getenv ( "SHELL" ); + + if ( opt_shell && getuid ( ) && 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; + } + + if ( !opt_shell ) + opt_shell = bb_xstrdup ( pw-> pw_shell ); + + 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; +} diff --git a/release/src/router/busybox/loginutils/sulogin.c b/release/src/router/busybox/loginutils/sulogin.c new file mode 100644 index 00000000..bb4716e0 --- /dev/null +++ b/release/src/router/busybox/loginutils/sulogin.c @@ -0,0 +1,184 @@ +/* vi: set sw=4 ts=4: */ +#include <fcntl.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <syslog.h> +#include <termios.h> +#include <unistd.h> +#include <utmp.h> +#include <sys/resource.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/types.h> +#include <ctype.h> +#include <time.h> + +#include "busybox.h" + + +// sulogin defines +#define SULOGIN_PROMPT "\nGive root password for system maintenance\n" \ + "(or type Control-D for normal startup):" + +static const char *forbid[] = { + "ENV", + "BASH_ENV", + "HOME", + "IFS", + "PATH", + "SHELL", + "LD_LIBRARY_PATH", + "LD_PRELOAD", + "LD_TRACE_LOADED_OBJECTS", + "LD_BIND_NOW", + "LD_AOUT_LIBRARY_PATH", + "LD_AOUT_PRELOAD", + "LD_NOWARN", + "LD_KEEPDIR", + (char *) 0 +}; + + + +static void catchalarm(int junk) +{ + exit(EXIT_FAILURE); +} + + +extern int sulogin_main(int argc, 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"); + } + if (!isatty(0) || !isatty(1) || !isatty(2)) { + exit(EXIT_FAILURE); + } + + + /* Clear out anything dangerous from the environment */ + for (p = forbid; *p; p++) + unsetenv(*p); + + + 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"); + } + 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; + } + } +#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ + while (1) { + cp = getpass(SULOGIN_PROMPT); + 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)); + } + if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 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"); + } + 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); +} diff --git a/release/src/router/busybox/loginutils/vlock.c b/release/src/router/busybox/loginutils/vlock.c new file mode 100644 index 00000000..7abf120d --- /dev/null +++ b/release/src/router/busybox/loginutils/vlock.c @@ -0,0 +1,233 @@ +/* vi: set sw=4 ts=4: */ +/* + * vlock implementation for busybox + * + * Copyright (C) 2000 by spoon <spoon@ix.netcom.com> + * Written by spoon <spon@ix.netcom.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +/* Shoutz to Michael K. Johnson <johnsonm@redhat.com>, author of the + * original vlock. I snagged a bunch of his code to write this + * minimalistic vlock. + */ +/* Fixed by Erik Andersen to do passwords the tinylogin way... + * It now works with md5, sha1, etc passwords. */ + +#include <stdio.h> +#include <stdlib.h> +#include <sys/vt.h> +#include <signal.h> +#include <string.h> +#include <unistd.h> +#include <fcntl.h> +#include <errno.h> +#include <sys/ioctl.h> +#include <termios.h> + +#include "busybox.h" + +static struct passwd *pw; +static struct vt_mode ovtm; +static struct termios oterm; +static int vfd; +static int o_lock_all = 0; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS +static struct spwd *spw; + +/* getspuid - get a shadow entry by uid */ +struct spwd *getspuid(uid_t uid) +{ + struct spwd *sp; + struct passwd *mypw; + + if ((mypw = getpwuid(getuid())) == NULL) { + return (NULL); + } + setspent(); + while ((sp = getspent()) != NULL) { + if (strcmp(mypw->pw_name, sp->sp_namp) == 0) + break; + } + endspent(); + return (sp); +} +#endif + +static void release_vt(int signo) +{ + if (!o_lock_all) + ioctl(vfd, VT_RELDISP, 1); + else + ioctl(vfd, VT_RELDISP, 0); +} + +static void acquire_vt(int signo) +{ + ioctl(vfd, VT_RELDISP, VT_ACKACQ); +} + +static void restore_terminal(void) +{ + 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); + + ovtm = vtm; + vtm.mode = VT_PROCESS; + vtm.relsig = SIGUSR1; + vtm.acqsig = SIGUSR2; + ioctl(vfd, VT_SETMODE, &vtm); + + tcgetattr(STDIN_FILENO, &oterm); + term = oterm; + term.c_iflag &= ~BRKINT; + term.c_iflag |= IGNBRK; + term.c_lflag &= ~ISIG; + term.c_lflag &= ~(ECHO | ECHOCTL); + tcsetattr(STDIN_FILENO, 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; + } + } while (1); +} + +/* +Local Variables: +c-file-style: "linux" +c-basic-offset: 4 +tab-width: 4 +End: +*/ |