summaryrefslogtreecommitdiff
path: root/i/pc104/initrd/conf/busybox/loginutils
diff options
context:
space:
mode:
Diffstat (limited to 'i/pc104/initrd/conf/busybox/loginutils')
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/Config.in193
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/Kbuild17
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/addgroup.c111
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/adduser.c196
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/deluser.c83
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/getty.c856
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/login.c403
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/passwd.c347
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/su.c103
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/sulogin.c126
-rw-r--r--i/pc104/initrd/conf/busybox/loginutils/vlock.c119
11 files changed, 2554 insertions, 0 deletions
diff --git a/i/pc104/initrd/conf/busybox/loginutils/Config.in b/i/pc104/initrd/conf/busybox/loginutils/Config.in
new file mode 100644
index 0000000..03a638c
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/Config.in
@@ -0,0 +1,193 @@
+#
+# For a description of the syntax of this configuration file,
+# see scripts/kbuild/config-language.txt.
+#
+
+menu "Login/Password Management Utilities"
+
+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_SHADOW
+ bool " Use busybox 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_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 ADDGROUP
+ bool "addgroup"
+ default n
+ help
+ Utility for creating a new group account.
+
+config DELGROUP
+ bool "delgroup"
+ default n
+ help
+ Utility for deleting a group account.
+
+config ADDUSER
+ bool "adduser"
+ default n
+ help
+ Utility for creating a new user account.
+
+config DELUSER
+ bool "deluser"
+ default n
+ help
+ Utility for deleting a user account.
+
+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
+ The file /var/run/utmp is used to track who is currently logged in.
+
+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.
+
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+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_SECURETTY
+ bool "Support for /etc/securetty"
+ default y
+ 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.
+
+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
+ may change the password for the group.
+
+ 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 SU
+ bool "su"
+ default n
+ select FEATURE_SUID
+ select FEATURE_SYSLOG
+ help
+ su is used to become another user during a login session.
+ Invoked without a username, su defaults to becoming the super user.
+
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+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
+ select FEATURE_SYSLOG
+ help
+ sulogin is invoked when the system goes into single user
+ mode (this is done through an entry in inittab).
+
+config VLOCK
+ bool "vlock"
+ default n
+ select FEATURE_SUID
+ help
+ Build the "vlock" applet which allows you to lock (virtual) terminals.
+
+ Note that Busybox binary must be setuid root for this applet to
+ work properly.
+
+endmenu
+
diff --git a/i/pc104/initrd/conf/busybox/loginutils/Kbuild b/i/pc104/initrd/conf/busybox/loginutils/Kbuild
new file mode 100644
index 0000000..6c9d193
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/Kbuild
@@ -0,0 +1,17 @@
+# Makefile for busybox
+#
+# Copyright (C) 1999-2005 by Erik Andersen <andersen@codepoet.org>
+#
+# Licensed under the GPL v2, see the file LICENSE in this tarball.
+
+lib-y:=
+lib-$(CONFIG_ADDGROUP) += addgroup.o
+lib-$(CONFIG_ADDUSER) += adduser.o
+lib-$(CONFIG_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/i/pc104/initrd/conf/busybox/loginutils/addgroup.c b/i/pc104/initrd/conf/busybox/loginutils/addgroup.c
new file mode 100644
index 0000000..78250a4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/addgroup.c
@@ -0,0 +1,111 @@
+/* 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>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "busybox.h"
+
+/* make sure gr_name isn't taken, make sure gid is kosher
+ * return 1 on failure */
+static int group_study(struct group *g)
+{
+ enum { max = 65000 };
+ FILE *etc_group;
+ gid_t desired;
+ /* Using _r function to avoid static buffers pulled in */
+ char buffer[256];
+ struct group grp;
+ struct group *result;
+
+ etc_group = xfopen(bb_path_group_file, "r");
+
+ /* make sure gr_name isn't taken, make sure gid is kosher */
+ desired = g->gr_gid;
+ while (!fgetgrent_r(etc_group, &grp, buffer, sizeof(buffer), &result)) {
+ if ((strcmp(grp.gr_name, g->gr_name)) == 0) {
+ bb_error_msg_and_die("%s: group already in use", g->gr_name);
+ }
+ if ((desired) && grp.gr_gid == desired) {
+ bb_error_msg_and_die("%d: gid already in use",
+ desired);
+ }
+ if ((grp.gr_gid > g->gr_gid) && (grp.gr_gid < max)) {
+ g->gr_gid = grp.gr_gid;
+ }
+ }
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(etc_group);
+
+ /* gid */
+ g->gr_gid++;
+ if (desired) {
+ g->gr_gid = desired;
+ }
+ /* return 1; */
+ return 0;
+}
+
+/* append a new user to the passwd file */
+static int addgroup(char *group, gid_t gid, const char *user)
+{
+ FILE *file;
+ struct group gr;
+
+ /* make sure gid and group haven't already been allocated */
+ gr.gr_gid = gid;
+ gr.gr_name = group;
+ if (group_study(&gr))
+ return 1;
+
+ /* add entry to group */
+ file = xfopen(bb_path_group_file, "a");
+ /* group:passwd:gid:userlist */
+ fprintf(file, "%s:%s:%d:%s\n", group, "x", gr.gr_gid, user);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(file);
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ file = fopen_or_warn(bb_path_gshadow_file, "a");
+ if (file) {
+ fprintf(file, "%s:!::\n", group);
+ if (ENABLE_FEATURE_CLEAN_UP)
+ fclose(file);
+ }
+#endif
+
+ /* return 1; */
+ return 0;
+}
+
+/*
+ * 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);
+int addgroup_main(int argc, char **argv)
+{
+ char *group;
+ gid_t gid = 0;
+
+ /* check for min, max and missing args and exit on error */
+ opt_complementary = "-1:?2:?";
+ if (getopt32(argc, argv, "g:", &group)) {
+ gid = xatoul_range(group, 0, (gid_t)ULONG_MAX);
+ }
+ /* move past the commandline options */
+ argv += optind;
+
+ /* need to be root */
+ if (geteuid()) {
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ }
+
+ return addgroup(argv[0], gid, argv[1] ? argv[1] : "");
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/adduser.c b/i/pc104/initrd/conf/busybox/loginutils/adduser.c
new file mode 100644
index 0000000..4c03790
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/adduser.c
@@ -0,0 +1,196 @@
+/* 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>
+ *
+ * Licensed under the GPL v2 or later, see the file LICENSE in this tarball.
+ */
+
+#include "busybox.h"
+
+#define OPT_DONT_SET_PASS (1 << 4)
+#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)
+{
+ enum { min = 500, max = 65000 };
+ FILE *passwd;
+ /* We are using reentrant fgetpwent_r() in order to avoid
+ * pulling in static buffers from libc (think static build here) */
+ char buffer[256];
+ struct passwd pw;
+ struct passwd *result;
+
+ passwd = xfopen(filename, "r");
+
+ /* 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 (!fgetpwent_r(passwd, &pw, buffer, sizeof(buffer), &result)) {
+ 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(struct passwd *p)
+{
+ char *cmd;
+
+ cmd = xasprintf("addgroup -g %d \"%s\"", 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)
+{
+ static const char prog[] = "passwd";
+ BB_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(struct passwd *p)
+{
+ FILE *file;
+ int addgroup = !p->pw_gid;
+
+ /* make sure everything is kosher and setup uid && gid */
+ file = xfopen(bb_path_passwd_file, "a");
+ fseek(file, 0, SEEK_END);
+
+ switch (passwd_study(bb_path_passwd_file, p)) {
+ case 1:
+ bb_error_msg_and_die("%s: login already in use", p->pw_name);
+ case 2:
+ bb_error_msg_and_die("illegal uid or no uids left");
+ case 3:
+ bb_error_msg_and_die("%s: group name already in use", p->pw_name);
+ }
+
+ /* add to passwd */
+ if (putpwent(p, file) == -1) {
+ bb_perror_nomsg_and_die();
+ }
+ fclose(file);
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* add to shadow if necessary */
+ file = xfopen(bb_path_shadow_file, "a");
+ fseek(file, 0, SEEK_END);
+ fprintf(file, "%s:!:%ld:%d:%d:%d:::\n",
+ p->pw_name, /* username */
+ time(NULL) / 86400, /* sp->sp_lstchg */
+ 0, /* sp->sp_min */
+ 99999, /* sp->sp_max */
+ 7); /* sp->sp_warn */
+ fclose(file);
+#endif
+
+ /* add to group */
+ /* addgroup should be responsible for dealing w/ gshadow */
+ /* if using a pre-existing group, don't create one */
+ if (addgroup) addgroup_wrapper(p);
+
+ /* 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(p->pw_dir, 0755)
+ || chown(p->pw_dir, p->pw_uid, p->pw_gid)
+ || chmod(p->pw_dir, 02755)) {
+ bb_perror_msg("%s", p->pw_dir);
+ }
+ }
+
+ if (!(option_mask32 & OPT_DONT_SET_PASS)) {
+ /* interactively set passwd */
+ passwd_wrapper(p->pw_name);
+ }
+
+ return 0;
+}
+
+/*
+ * 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);
+int adduser_main(int argc, char **argv)
+{
+ struct passwd pw;
+ const char *usegroup = NULL;
+
+ /* got root? */
+ if (geteuid()) {
+ bb_error_msg_and_die(bb_msg_perm_denied_are_you_root);
+ }
+
+ pw.pw_gecos = (char *)"Linux User,,,";
+ pw.pw_shell = (char *)DEFAULT_SHELL;
+ pw.pw_dir = NULL;
+
+ /* check for min, max and missing args and exit on error */
+ opt_complementary = "-1:?1:?";
+ getopt32(argc, argv, "h:g:s:G:DSH", &pw.pw_dir, &pw.pw_gecos, &pw.pw_shell, &usegroup);
+
+ /* create string for $HOME if not specified already */
+ if (!pw.pw_dir) {
+ snprintf(bb_common_bufsiz1, BUFSIZ, "/home/%s", argv[optind]);
+ pw.pw_dir = bb_common_bufsiz1;
+ }
+
+ /* create a passwd struct */
+ pw.pw_name = argv[optind];
+ pw.pw_passwd = (char *)"x";
+ pw.pw_uid = 0;
+ pw.pw_gid = usegroup ? xgroup2gid(usegroup) : 0; /* exits on failure */
+
+ /* grand finale */
+ return adduser(&pw);
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/deluser.c b/i/pc104/initrd/conf/busybox/loginutils/deluser.c
new file mode 100644
index 0000000..e9bde00
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/deluser.c
@@ -0,0 +1,83 @@
+/* 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>
+ * Unified with delgroup by Tito Ragusa <farmatito@tiscali.it>
+ *
+ * Licensed under GPL version 2, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "busybox.h"
+
+static void del_line_matching(const char *login, const char *filename)
+{
+ char *line;
+ FILE *passwd;
+ int len = strlen(login);
+ int found = 0;
+ llist_t *plist = NULL;
+
+ passwd = fopen_or_warn(filename, "r");
+ if (!passwd) return;
+
+ while ((line = xmalloc_fgets(passwd))) {
+ if (!strncmp(line, login, len)
+ && line[len] == ':'
+ ) {
+ found++;
+ free(line);
+ } else {
+ llist_add_to_end(&plist, line);
+ }
+ }
+
+ if (!ENABLE_FEATURE_CLEAN_UP) {
+ if (!found) {
+ bb_error_msg("can't find '%s' in '%s'", login, filename);
+ return;
+ }
+ passwd = fopen_or_warn(filename, "w");
+ if (passwd)
+ while ((line = llist_pop(&plist)))
+ fputs(line, passwd);
+ } else {
+ if (!found) {
+ bb_error_msg("can't find '%s' in '%s'", login, filename);
+ goto clean_up;
+ }
+ fclose(passwd);
+ passwd = fopen_or_warn(filename, "w");
+ if (passwd) {
+ clean_up:
+ while ((line = llist_pop(&plist))) {
+ if (found) fputs(line, passwd);
+ free(line);
+ }
+ fclose(passwd);
+ }
+ }
+}
+
+int deluser_main(int argc, char **argv);
+int deluser_main(int argc, char **argv)
+{
+ if (argc != 2)
+ bb_show_usage();
+
+ if (ENABLE_DELUSER
+ && (!ENABLE_DELGROUP || applet_name[3] == 'u')
+ ) {
+ del_line_matching(argv[1], bb_path_passwd_file);
+ if (ENABLE_FEATURE_SHADOWPASSWDS)
+ del_line_matching(argv[1], bb_path_shadow_file);
+ }
+ del_line_matching(argv[1], bb_path_group_file);
+ if (ENABLE_FEATURE_SHADOWPASSWDS)
+ del_line_matching(argv[1], bb_path_gshadow_file);
+
+ return EXIT_SUCCESS;
+}
+
diff --git a/i/pc104/initrd/conf/busybox/loginutils/getty.c b/i/pc104/initrd/conf/busybox/loginutils/getty.c
new file mode 100644
index 0000000..64d2d08
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/getty.c
@@ -0,0 +1,856 @@
+/* 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.
+ *
+ * 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
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ *
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+
+#if ENABLE_FEATURE_UTMP
+#include <utmp.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 */
+#include <sys/utsname.h>
+#include <time.h>
+#if ENABLE_FEATURE_WTMP
+extern void updwtmp(const char *filename, const struct utmp *ut);
+static void update_utmp(const char *line);
+#endif
+#endif /* LOGIN_PROCESS */
+
+/*
+ * 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.
+ */
+
+/* I doubt there are systems which still need this */
+#undef HANDLE_ALLCAPS
+
+#define _PATH_LOGIN "/bin/login"
+
+/* 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 */
+
+/* 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 '\n'
+#define DEF_SWITCH 0 /* default switch char */
+
+/*
+ * 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 */
+ 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 */
+};
+
+static const char opt_string[] = "I:LH:f:hil:mt:wn";
+#define F_INITSTRING (1<<0) /* initstring is set */
+#define F_LOCAL (1<<1) /* force local */
+#define F_FAKEHOST (1<<2) /* force fakehost */
+#define F_CUSTISSUE (1<<3) /* give alternative issue file */
+#define F_RTSCTS (1<<4) /* enable RTS/CTS flow control */
+#define F_ISSUE (1<<5) /* display /etc/issue */
+#define F_LOGIN (1<<6) /* non-default login program */
+#define F_PARSE (1<<7) /* process modem status messages */
+#define F_TIMEOUT (1<<8) /* time out */
+#define F_WAITCRLF (1<<9) /* wait for CR or LF */
+#define F_NOPROMPT (1<<10) /* don't ask for login name! */
+
+/* Storage for things detected while the login name was read. */
+struct chardata {
+ 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 */
+#ifdef HANDLE_ALLCAPS
+ unsigned char capslock; /* upper case without lower case */
+#endif
+};
+
+/* 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
+};
+
+/* 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"
+static FILE *dbf;
+#else
+#define debug(s) /* nothing */
+#endif
+
+
+/* bcode - convert speed string to speed code; return 0 on failure */
+static int bcode(const char *s)
+{
+ int r;
+ unsigned value = bb_strtou(s, NULL, 10);
+ if (errno) {
+ return -1;
+ }
+ r = tty_value_to_baud(value);
+ if (r > 0) {
+ return r;
+ }
+ return 0;
+}
+
+
+/* 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)
+ bb_error_msg_and_die("bad speed: %s", cp);
+ if (op->numspeed > MAX_SPEED)
+ bb_error_msg_and_die("too many alternate speeds");
+ }
+ debug("exiting parsespeeds\n");
+}
+
+
+/* parse_args - parse command-line arguments */
+static void parse_args(int argc, char **argv, struct options *op)
+{
+ char *ts;
+
+ op->flags = getopt32(argc, argv, opt_string,
+ &(op->initstring), &fakehost, &(op->issue),
+ &(op->login), &ts);
+ if (op->flags & F_INITSTRING) {
+ const char *p = op->initstring;
+ char *q;
+
+ op->initstring = q = xstrdup(op->initstring);
+ /* 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; /* revert flag show /etc/issue */
+ if (op->flags & F_TIMEOUT) {
+ op->timeout = xatoul_range(ts, 1, INT_MAX);
+ }
+ argv += optind;
+ argc -= optind;
+ debug("after getopt loop\n");
+ if (argc < 2) /* check parameter count */
+ bb_show_usage();
+
+ /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
+ if (isdigit(argv[0][0])) {
+ /* a number first, assume it's a speed (BSD style) */
+ parse_speeds(op, argv[0]); /* baud rate(s) */
+ op->tty = argv[1]; /* tty name */
+ } else {
+ op->tty = argv[0]; /* tty name */
+ parse_speeds(op, argv[1]); /* baud rate(s) */
+ }
+
+ if (argv[2])
+ setenv("TERM", argv[2], 1);
+
+ debug("exiting parseargs\n");
+}
+
+static void xdup2(int srcfd, int dstfd, const char *tty)
+{
+ if (dup2(srcfd, dstfd) == -1)
+ bb_perror_msg_and_die("%s: dup", tty);
+}
+
+/* open_tty - set up tty as standard { input, output, error } */
+static void open_tty(const char *tty, struct termios *tp, int local)
+{
+ int chdir_to_root = 0;
+
+ /* Set up new standard input, unless we are given an already opened port. */
+
+ if (NOT_LONE_DASH(tty)) {
+ struct stat st;
+ int fd;
+
+ /* Sanity checks... */
+
+ xchdir("/dev");
+ chdir_to_root = 1;
+ xstat(tty, &st);
+ if ((st.st_mode & S_IFMT) != S_IFCHR)
+ bb_error_msg_and_die("%s: not a character device", tty);
+
+ /* Open the tty as standard input. */
+
+ debug("open(2)\n");
+ fd = xopen(tty, O_RDWR | O_NONBLOCK);
+ xdup2(fd, 0, tty);
+ while (fd > 2) close(fd--);
+ } 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)
+ bb_error_msg_and_die("stdin is not open for read/write");
+ }
+
+ /* Replace current standard output/error fd's with new ones */
+ debug("duping\n");
+ xdup2(0, 1, tty);
+ xdup2(0, 2, tty);
+
+ /*
+ * 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, TCGETS, tp) < 0)
+ bb_perror_msg_and_die("%s: ioctl(TCGETS)", 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
+#warning Debian /dev/vcs[a]NN hack is deprecated and will be removed
+ {
+ /* tty to root.dialout 660 */
+ struct group *gr;
+ int id;
+
+ gr = getgrnam("dialout");
+ id = gr ? 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;
+
+ vcs = xstrdup(tty);
+ vcsa = xmalloc(strlen(tty) + 2);
+ strcpy(vcs, "vcs");
+ strcpy(vcs + 3, tty + 3);
+ strcpy(vcsa, "vcsa");
+ strcpy(vcsa + 4, tty + 3);
+
+ gr = getgrnam("sys");
+ id = gr ? gr->gr_gid : 0;
+ chown(vcs, 0, id);
+ chmod(vcs, 0600);
+ chown(vcsa, 0, id);
+ chmod(vcs, 0600);
+
+ free(vcs);
+ free(vcsa);
+ }
+ }
+#else
+ if (NOT_LONE_DASH(tty)) {
+ chown(tty, 0, 0); /* 0:0 */
+ chmod(tty, 0622); /* crw--w--w- */
+ }
+#endif
+ if (chdir_to_root)
+ xchdir("/");
+}
+
+/* termios_init - initialize termios settings */
+static void termios_init(struct termios *tp, int speed, struct options *op)
+{
+ /*
+ * 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
+ * later on.
+ */
+#ifdef __linux__
+ /* flush input and output queues, important for modems! */
+ 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_line = 0;
+ tp->c_oflag = OPOST | ONLCR;
+ 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
+
+ ioctl(0, TCSETS, 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(char *buf, unsigned size_buf, struct termios *tp)
+{
+ int speed;
+ int vmin;
+ unsigned iflag;
+ 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 later on.
+ */
+
+ 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 */
+ ioctl(0, TCSETS, 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.
+ */
+
+ sleep(1);
+ nread = read(0, buf, size_buf - 1);
+ if (nread > 0) {
+ buf[nread] = '\0';
+ for (bp = buf; bp < buf + nread; bp++) {
+ if (isascii(*bp) && isdigit(*bp)) {
+ speed = bcode(bp);
+ if (speed) {
+ tp->c_cflag &= ~CBAUD;
+ tp->c_cflag |= speed;
+ }
+ break;
+ }
+ }
+ }
+ /* Restore terminal settings. Errors will be dealt with later on. */
+
+ tp->c_iflag = iflag;
+ tp->c_cc[VMIN] = vmin;
+ ioctl(0, TCSETS, tp);
+}
+
+/* next_speed - select next baud rate */
+static void next_speed(struct termios *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];
+ ioctl(0, TCSETS, tp);
+}
+
+
+/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
+static void do_prompt(struct options *op, struct termios *tp)
+{
+#ifdef ISSUE
+ print_login_issue(op->issue, op->tty);
+#endif
+ print_login_prompt();
+}
+
+#ifdef HANDLE_ALLCAPS
+/* caps_lock - string contains upper case without lower case */
+/* returns 1 if true, 0 if false */
+static int caps_lock(const char *s)
+{
+ 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(char *logname, unsigned size_logname,
+ struct options *op, struct chardata *cp, struct termios *tp)
+{
+ 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 const char erase[][3] = { /* 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). */
+
+ sleep(1);
+ ioctl(0, TCFLSH, TCIFLUSH);
+
+ /* Prompt for and read a login name. */
+
+ logname[0] = '\0';
+ while (!logname[0]) {
+
+ /* Write issue file and prompt, with "parity" bit == 0. */
+
+ do_prompt(op, tp);
+
+ /* Read name, watch for break, parity, erase, kill, end-of-line. */
+
+ bp = logname;
+ cp->eol = '\0';
+ while (cp->eol == '\0') {
+
+ /* Do not report trivial EINTR/EIO errors. */
+ if (read(0, &c, 1) < 1) {
+ if (errno == EINTR || errno == EIO)
+ exit(0);
+ bb_perror_msg_and_die("%s: read", op->tty);
+ }
+
+ /* Do BREAK handling elsewhere. */
+ if (c == '\0' && op->numspeed > 1)
+ return NULL;
+
+ /* Do parity bit handling. */
+ ascval = c & 0177;
+ if (c != ascval) { /* "parity" bit on ? */
+ bits = 1;
+ mask = 1;
+ while (mask & 0177) {
+ if (mask & ascval)
+ bits++; /* count "1" bits */
+ mask <<= 1;
+ }
+ /* ... |= 2 - even, 1 - odd */
+ cp->parity |= 2 - (bits & 1);
+ }
+
+ /* 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) {
+ write(1, erase[cp->parity], 3);
+ bp--;
+ }
+ break;
+ case CTL('U'):
+ case '@':
+ cp->kill = ascval; /* set kill character */
+ while (bp > logname) {
+ 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 >= size_logname - 1) {
+ bb_error_msg_and_die("%s: input overrun", op->tty);
+ } else {
+ write(1, &c, 1); /* echo the character */
+ *bp++ = ascval; /* and store it */
+ }
+ break;
+ }
+ }
+ }
+ /* Handle names with upper case and no lower case. */
+
+#ifdef HANDLE_ALLCAPS
+ cp->capslock = caps_lock(logname);
+ if (cp->capslock) {
+ for (bp = logname; *bp; bp++)
+ if (isupper(*bp))
+ *bp = tolower(*bp); /* map name to lower case */
+ }
+#endif
+ return logname;
+}
+
+/* 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_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. */
+#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
+ if (op->flags & F_RTSCTS)
+ tp->c_cflag |= CRTSCTS;
+#endif
+
+ /* Finally, make the new settings effective */
+
+ if (ioctl(0, TCSETS, tp) < 0)
+ bb_perror_msg_and_die("%s: ioctl(TCSETS)", op->tty);
+}
+
+
+#ifdef SYSV_STYLE
+#if ENABLE_FEATURE_UTMP
+/* update_utmp - update our utmp entry */
+static void update_utmp(const char *line)
+{
+ struct utmp ut;
+ struct utmp *utp;
+ time_t t;
+ int mypid = getpid();
+
+ /*
+ * 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.
+ */
+ if (access(_PATH_UTMP, R_OK|W_OK) == -1) {
+ close(creat(_PATH_UTMP, 0664));
+ }
+ 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));
+ safe_strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
+ }
+ /* endutent(); */
+
+ 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));
+ time(&t);
+ ut.ut_time = t;
+ ut.ut_type = LOGIN_PROCESS;
+ ut.ut_pid = mypid;
+
+ pututline(&ut);
+ 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, &ut);
+#endif
+}
+
+#endif /* CONFIG_FEATURE_UTMP */
+#endif /* SYSV_STYLE */
+
+
+int getty_main(int argc, char **argv);
+int getty_main(int argc, char **argv)
+{
+ int nullfd;
+ char *logname = NULL; /* 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 = {
+ 0, /* show /etc/issue (SYSV_STYLE) */
+ 0, /* no timeout */
+ _PATH_LOGIN, /* default login program */
+ "tty1", /* default tty line */
+ "", /* modem init string */
+#ifdef ISSUE
+ ISSUE, /* default issue file */
+#else
+ NULL,
+#endif
+ 0, /* no baud rates known yet */
+ };
+
+ /* Already too late because of theoretical
+ * possibility of getty --help somehow triggered
+ * inadvertently before we reach this. Oh well. */
+ logmode = LOGMODE_NONE;
+ setsid();
+ nullfd = xopen(bb_dev_null, O_RDWR);
+ /* dup2(nullfd, 0); - no, because of possible "getty - 9600" */
+ /* open_tty() will take care of fd# 0 anyway */
+ dup2(nullfd, 1);
+ dup2(nullfd, 2);
+ while (nullfd > 2) close(nullfd--);
+ /* We want special flavor of error_msg_and_die */
+ die_sleep = 10;
+ msg_eol = "\r\n";
+ openlog(applet_name, LOG_PID, LOG_AUTH);
+ logmode = LOGMODE_BOTH;
+
+#ifdef DEBUGGING
+ dbf = 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 SYSV_STYLE
+#if ENABLE_FEATURE_UTMP
+ /* Update the utmp file. */
+ update_utmp(options.tty);
+#endif
+#endif
+
+ debug("calling open_tty\n");
+ /* Open the tty as standard { input, output, error }. */
+ open_tty(options.tty, &termios, options.flags & F_LOCAL);
+
+#ifdef __linux__
+ {
+ int iv;
+
+ iv = getpid();
+ ioctl(0, TIOCSPGRP, &iv);
+ }
+#endif
+ /* Initialize the termios settings (raw mode, eight-bit, blocking i/o). */
+ debug("calling termios_init\n");
+ termios_init(&termios, 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(bb_common_bufsiz1, sizeof(bb_common_bufsiz1), &termios);
+
+ /* Set the optional timer. */
+ if (options.timeout)
+ alarm(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");
+ logname = get_logname(bb_common_bufsiz1, sizeof(bb_common_bufsiz1),
+ &options, &chardata, &termios);
+ while (logname == NULL)
+ next_speed(&termios, &options);
+ }
+
+ /* Disable timer. */
+
+ if (options.timeout)
+ alarm(0);
+
+ /* Finalize the termios settings. */
+
+ termios_final(&options, &termios, &chardata);
+
+ /* Now the newline character should be properly written. */
+
+ write(1, "\n", 1);
+
+ /* Let the login program take care of password validation. */
+
+ execl(options.login, options.login, "--", logname, (char *) 0);
+ bb_error_msg_and_die("%s: can't exec %s", options.tty, options.login);
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/login.c b/i/pc104/initrd/conf/busybox/loginutils/login.c
new file mode 100644
index 0000000..830df0a
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/login.c
@@ -0,0 +1,403 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <utmp.h>
+#include <sys/resource.h>
+#include <syslog.h>
+
+#ifdef CONFIG_SELINUX
+#include <selinux/selinux.h> /* for is_selinux_enabled() */
+#include <selinux/get_context_list.h> /* for get_default_context() */
+#include <selinux/flask.h> /* for security class definitions */
+#include <errno.h>
+#endif
+
+enum {
+ TIMEOUT = 60,
+ EMPTY_USERNAME_COUNT = 10,
+ USERNAME_SIZE = 32,
+ TTYNAME_SIZE = 32,
+};
+
+static char full_tty[TTYNAME_SIZE];
+static char* short_tty = full_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.
+ */
+
+static struct utmp utent;
+
+static void read_or_build_utent(int picky)
+{
+ 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)
+ bb_error_msg_and_die("no utmp entry found");
+
+ memset(&utent, 0, sizeof(utent));
+ utent.ut_type = LOGIN_PROCESS;
+ utent.ut_pid = pid;
+ strncpy(utent.ut_line, short_tty, sizeof(utent.ut_line));
+ /* This one is only 4 chars wide. Try to fit something
+ * remotely meaningful by skipping "tty"... */
+ strncpy(utent.ut_id, short_tty + 3, sizeof(utent.ut_id));
+ strncpy(utent.ut_user, "LOGIN", sizeof(utent.ut_user));
+ utent.ut_time = time(NULL);
+ }
+ if (!picky) /* root login */
+ memset(utent.ut_host, 0, sizeof(utent.ut_host));
+}
+
+/*
+ * 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(const char *username)
+{
+ utent.ut_type = USER_PROCESS;
+ strncpy(utent.ut_user, username, sizeof(utent.ut_user));
+ utent.ut_time = time(NULL);
+ /* other fields already filled in by read_or_build_utent above */
+ setutent();
+ pututline(&utent);
+ 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, &utent);
+#endif
+}
+#else /* !ENABLE_FEATURE_UTMP */
+static inline void read_or_build_utent(int ATTRIBUTE_UNUSED picky) {}
+static inline void write_utent(const char ATTRIBUTE_UNUSED *username) {}
+#endif /* !ENABLE_FEATURE_UTMP */
+
+static void die_if_nologin_and_non_root(int amroot)
+{
+ FILE *fp;
+ int c;
+
+ if (access(bb_path_nologin_file, F_OK))
+ return;
+
+ fp = fopen(bb_path_nologin_file, "r");
+ if (fp) {
+ 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)
+ exit(1);
+ puts("\r\n[Disconnect bypassed -- root login allowed.]\r");
+}
+
+#if ENABLE_FEATURE_SECURETTY
+static int check_securetty(void)
+{
+ FILE *fp;
+ int i;
+ char buf[BUFSIZ];
+
+ fp = fopen(bb_path_securetty_file, "r");
+ if (!fp) {
+ /* A missing securetty file is not an error. */
+ return 1;
+ }
+ while (fgets(buf, sizeof(buf)-1, fp)) {
+ for (i = strlen(buf)-1; i>=0; --i) {
+ if (!isspace(buf[i]))
+ break;
+ }
+ buf[++i] = '\0';
+ if ((buf[0]=='\0') || (buf[0]=='#'))
+ continue;
+ if (strcmp(buf, short_tty) == 0) {
+ fclose(fp);
+ return 1;
+ }
+ }
+ fclose(fp);
+ return 0;
+}
+#else
+static inline int check_securetty(void) { return 1; }
+#endif
+
+static void get_username_or_die(char *buf, int size_buf)
+{
+ int c, cntdown;
+ cntdown = EMPTY_USERNAME_COUNT;
+prompt:
+ /* skip whitespace */
+ print_login_prompt();
+ do {
+ c = getchar();
+ if (c == EOF) exit(1);
+ if (c == '\n') {
+ if (!--cntdown) exit(1);
+ goto prompt;
+ }
+ } while (isspace(c));
+
+ *buf++ = c;
+ if (!fgets(buf, size_buf-2, stdin))
+ exit(1);
+ if (!strchr(buf, '\n'))
+ exit(1);
+ while (isgraph(*buf)) buf++;
+ *buf = '\0';
+}
+
+static void motd(void)
+{
+ FILE *fp;
+ int c;
+
+ fp = fopen(bb_path_motd_file, "r");
+ if (fp) {
+ while ((c = getc(fp)) != EOF)
+ putchar(c);
+ fclose(fp);
+ }
+}
+
+static void nonblock(int fd)
+{
+ fcntl(fd, F_SETFL, O_NONBLOCK | fcntl(fd, F_GETFL));
+}
+
+static void alarm_handler(int sig ATTRIBUTE_UNUSED)
+{
+ /* 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 */
+ nonblock(1);
+ nonblock(2);
+ bb_info_msg("\r\nLogin timed out after %d seconds\r", TIMEOUT);
+ exit(EXIT_SUCCESS);
+}
+
+int login_main(int argc, char **argv);
+int login_main(int argc, char **argv)
+{
+ enum {
+ LOGIN_OPT_f = (1<<0),
+ LOGIN_OPT_h = (1<<1),
+ LOGIN_OPT_p = (1<<2),
+ };
+ char fromhost[512];
+ char username[USERNAME_SIZE];
+ const char *tmp;
+ int amroot;
+ unsigned opt;
+ int count = 0;
+ struct passwd *pw;
+ char *opt_host = NULL;
+ char *opt_user = NULL;
+ USE_SELINUX(security_context_t user_sid = NULL;)
+
+ username[0] = '\0';
+ amroot = (getuid() == 0);
+ signal(SIGALRM, alarm_handler);
+ alarm(TIMEOUT);
+
+ opt = getopt32(argc, argv, "f:h:p", &opt_user, &opt_host);
+ if (opt & LOGIN_OPT_f) {
+ if (!amroot)
+ bb_error_msg_and_die("-f is for root only");
+ safe_strncpy(username, opt_user, sizeof(username));
+ }
+ if (optind < argc) /* user from command line (getty) */
+ safe_strncpy(username, argv[optind], sizeof(username));
+
+ /* Let's find out and memorize our tty */
+ if (!isatty(0) || !isatty(1) || !isatty(2))
+ return EXIT_FAILURE; /* Must be a terminal */
+ safe_strncpy(full_tty, "UNKNOWN", sizeof(full_tty));
+ tmp = ttyname(0);
+ if (tmp) {
+ safe_strncpy(full_tty, tmp, sizeof(full_tty));
+ if (strncmp(full_tty, "/dev/", 5) == 0)
+ short_tty = full_tty + 5;
+ }
+
+ read_or_build_utent(!amroot);
+
+ if (opt_host) {
+ USE_FEATURE_UTMP(
+ safe_strncpy(utent.ut_host, opt_host, sizeof(utent.ut_host));
+ )
+ snprintf(fromhost, sizeof(fromhost)-1, " on '%.100s' from "
+ "'%.200s'", short_tty, opt_host);
+ }
+ else
+ snprintf(fromhost, sizeof(fromhost)-1, " on '%.100s'", short_tty);
+
+ bb_setpgrp;
+
+ openlog(applet_name, LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
+
+ while (1) {
+ if (!username[0])
+ get_username_or_die(username, sizeof(username));
+
+ pw = getpwnam(username);
+ if (!pw) {
+ safe_strncpy(username, "UNKNOWN", sizeof(username));
+ goto auth_failed;
+ }
+
+ if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*')
+ goto auth_failed;
+
+ 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;
+
+ /* authorization takes place here */
+ if (correct_password(pw))
+ break;
+
+auth_failed:
+ opt &= ~LOGIN_OPT_f;
+ bb_do_delay(FAIL_DELAY);
+ puts("Login incorrect");
+ if (++count == 3) {
+ syslog(LOG_WARNING, "invalid password for '%s'%s",
+ username, fromhost);
+ return EXIT_FAILURE;
+ }
+ username[0] = '\0';
+ }
+
+ alarm(0);
+ die_if_nologin_and_non_root(pw->pw_uid == 0);
+
+ write_utent(username);
+
+#ifdef CONFIG_SELINUX
+ if (is_selinux_enabled()) {
+ security_context_t old_tty_sid, new_tty_sid;
+
+ 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);
+ }
+ 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);
+ }
+ }
+#endif
+ /* 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);
+
+ /* TODO: be nommu-friendly, use spawn? */
+ if (ENABLE_LOGIN_SCRIPTS) {
+ char *script = getenv("LOGIN_PRE_SUID_SCRIPT");
+ if (script) {
+ char *t_argv[2] = { script, NULL };
+ switch (fork()) {
+ case -1: break;
+ case 0: /* child */
+ xchdir("/");
+ setenv("LOGIN_TTY", full_tty, 1);
+ setenv("LOGIN_USER", pw->pw_name, 1);
+ setenv("LOGIN_UID", utoa(pw->pw_uid), 1);
+ setenv("LOGIN_GID", utoa(pw->pw_gid), 1);
+ setenv("LOGIN_SHELL", pw->pw_shell, 1);
+ BB_EXECVP(script, t_argv);
+ exit(1);
+ default: /* parent */
+ wait(NULL);
+ }
+ }
+ }
+
+ change_identity(pw);
+ tmp = pw->pw_shell;
+ if (!tmp || !*tmp)
+ tmp = DEFAULT_SHELL;
+ setup_environment(tmp, 1, !(opt & LOGIN_OPT_p), pw);
+
+ motd();
+
+ if (pw->pw_uid == 0)
+ syslog(LOG_INFO, "root login%s", fromhost);
+#ifdef CONFIG_SELINUX
+ /* well, a simple setexeccon() here would do the job as well,
+ * but let's play the game for now */
+ set_current_security_context(user_sid);
+#endif
+
+ // util-linux login also does:
+ // /* start new session */
+ // setsid();
+ // /* TIOCSCTTY: steal tty from other process group */
+ // if (ioctl(0, TIOCSCTTY, 1)) error_msg...
+
+ /* set signals to defaults */
+ 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 it leave SIGINT etc enabled or disabled? */
+ signal(SIGINT, SIG_DFL);
+
+ run_shell(tmp, 1, 0, 0); /* exec the shell finally */
+
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/passwd.c b/i/pc104/initrd/conf/busybox/loginutils/passwd.c
new file mode 100644
index 0000000..b937ce4
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/passwd.c
@@ -0,0 +1,347 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include "busybox.h"
+#include <syslog.h>
+
+
+static void nuke_str(char *str)
+{
+ if (str) memset(str, 0, strlen(str));
+}
+
+
+static int i64c(int i)
+{
+ i &= 0x3f;
+ if (i == 0)
+ return '.';
+ if (i == 1)
+ return '/';
+ if (i < 12)
+ return ('0' - 2 + i);
+ if (i < 38)
+ return ('A' - 12 + i);
+ return ('a' - 38 + i);
+}
+
+
+static void crypt_make_salt(char *p, int cnt)
+{
+ unsigned x = x; /* it's pointless to initialize it anyway :) */
+
+ x += getpid() + time(NULL) + clock();
+ do {
+ /* x = (x*1664525 + 1013904223) % 2^32 generator is lame
+ * (low-order bit is not "random", etc...),
+ * but for our purposes it is good enough */
+ x = x*1664525 + 1013904223;
+ /* BTW, Park and Miller's "minimal standard generator" is
+ * x = x*16807 % ((2^31)-1)
+ * It has no problem with visibly alternating lowest bit
+ * but is also weak in cryptographic sense + needs div,
+ * which needs more code (and slower) on many CPUs */
+ *p++ = i64c(x >> 16);
+ *p++ = i64c(x >> 22);
+ } while (--cnt);
+ *p = '\0';
+}
+
+
+static char* new_password(const struct passwd *pw, uid_t myuid, int algo)
+{
+ char salt[sizeof("$N$XXXXXXXX")]; /* "$N$XXXXXXXX" or "XX" */
+ char *orig = (char*)"";
+ char *newp = NULL;
+ char *cipher = NULL;
+ char *cp = NULL;
+ char *ret = NULL; /* failure so far */
+
+ if (myuid && pw->pw_passwd[0]) {
+ orig = bb_askpass(0, "Old password:"); /* returns ptr to static */
+ if (!orig)
+ goto err_ret;
+ cipher = pw_encrypt(orig, pw->pw_passwd); /* returns ptr to static */
+ if (strcmp(cipher, 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;
+ }
+ }
+ orig = xstrdup(orig); /* or else bb_askpass() will destroy it */
+ newp = bb_askpass(0, "New password:"); /* returns ptr to static */
+ if (!newp)
+ goto err_ret;
+ newp = xstrdup(newp); /* we are going to bb_askpass() 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_askpass(0, "Retype password:");
+ if (!cp)
+ goto err_ret;
+ if (strcmp(cp, newp)) {
+ puts("Passwords don't match");
+ goto err_ret;
+ }
+
+ /*memset(salt, 0, sizeof(salt)); - why?*/
+ crypt_make_salt(salt, 1); /* des */
+ if (algo) { /* MD5 */
+ strcpy(salt, "$1$");
+ crypt_make_salt(salt + 3, 4);
+ }
+ ret = xstrdup(pw_encrypt(newp, salt)); /* returns ptr to static */
+ /* 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(cipher);
+ nuke_str(cp);
+ return ret;
+}
+
+
+#if 0
+static int get_algo(char *a)
+{
+ /* standard: MD5 */
+ int x = 1;
+ if (strcasecmp(a, "des") == 0)
+ x = 0;
+ return x;
+}
+#endif
+
+
+static int update_passwd(const char *filename, const char *username,
+ const char *new_pw)
+{
+ struct stat sb;
+ struct flock lock;
+ FILE *old_fp;
+ FILE *new_fp;
+ char *new_name;
+ char *last_char;
+ unsigned user_len;
+ int old_fd;
+ int new_fd;
+ int i;
+ int ret = 1; /* failure */
+
+ logmode = LOGMODE_STDIO;
+ /* New passwd file, "/etc/passwd+" for now */
+ new_name = xasprintf("%s+", filename);
+ last_char = &new_name[strlen(new_name)-1];
+ username = xasprintf("%s:", username);
+ user_len = strlen(username);
+
+ old_fp = fopen(filename, "r+");
+ if (!old_fp)
+ goto free_mem;
+ old_fd = fileno(old_fp);
+
+ /* Try to create "/etc/passwd+". Wait if it exists. */
+ i = 30;
+ do {
+ // FIXME: on last iteration try w/o O_EXCL but with O_TRUNC?
+ new_fd = open(new_name, O_WRONLY|O_CREAT|O_EXCL,0600);
+ if (new_fd >= 0) goto created;
+ if (errno != EEXIST) break;
+ usleep(100000); /* 0.1 sec */
+ } while (--i);
+ bb_perror_msg("cannot create '%s'", new_name);
+ goto close_old_fp;
+ created:
+ if (!fstat(old_fd, &sb)) {
+ fchmod(new_fd, sb.st_mode & 0777); /* ignore errors */
+ fchown(new_fd, sb.st_uid, sb.st_gid);
+ }
+ new_fp = fdopen(new_fd, "w");
+ if (!new_fp) {
+ close(new_fd);
+ goto unlink_new;
+ }
+
+ /* Backup file is "/etc/passwd-" */
+ last_char[0] = '-';
+ /* Delete old one, create new as a hardlink to current */
+ i = (unlink(new_name) && errno != ENOENT);
+ if (i || link(filename, new_name))
+ bb_perror_msg("warning: cannot create backup copy '%s'", new_name);
+ last_char[0] = '+';
+
+ /* 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(old_fd, F_SETLK, &lock) < 0)
+ bb_perror_msg("warning: cannot lock '%s'", filename);
+ lock.l_type = F_UNLCK;
+
+ /* Read current password file, write updated one */
+ while (1) {
+ char *line = xmalloc_fgets(old_fp);
+ if (!line) break; /* EOF/error */
+ if (strncmp(username, line, user_len) == 0) {
+ /* we have a match with "username:"... */
+ const char *cp = line + user_len;
+ /* now cp -> old passwd, skip it: */
+ cp = strchr(cp, ':');
+ if (!cp) cp = "";
+ /* now cp -> ':' after old passwd or -> "" */
+ fprintf(new_fp, "%s%s%s", username, new_pw, cp);
+ /* Erase password in memory */
+ } else
+ fputs(line, new_fp);
+ free(line);
+ }
+ fcntl(old_fd, F_SETLK, &lock);
+
+ /* We do want all of them to execute, thus | instead of || */
+ if ((ferror(old_fp) | fflush(new_fp) | fsync(new_fd) | fclose(new_fp))
+ || rename(new_name, filename)
+ ) {
+ /* At least one of those failed */
+ goto unlink_new;
+ }
+ ret = 0; /* whee, success! */
+
+ unlink_new:
+ if (ret) unlink(new_name);
+
+ close_old_fp:
+ fclose(old_fp);
+
+ free_mem:
+ if (ENABLE_FEATURE_CLEAN_UP) free(new_name);
+ if (ENABLE_FEATURE_CLEAN_UP) free((char*)username);
+ logmode = LOGMODE_BOTH;
+ return ret;
+}
+
+
+int passwd_main(int argc, char **argv);
+int passwd_main(int argc, char **argv)
+{
+ 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;
+ const char *opt_a = "";
+ const char *filename;
+ char *myname;
+ 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;
+ struct spwd *result;
+ char buffer[256];
+#endif
+
+ logmode = LOGMODE_BOTH;
+ openlog(applet_name, LOG_NOWAIT, LOG_AUTH);
+ opt = getopt32(argc, 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();
+ if ((opt & OPT_lud) && (!argc || myuid))
+ bb_show_usage();
+
+ myname = xstrdup(bb_getpwuid(NULL, myuid, -1));
+ name = argc ? argv[0] : myname;
+
+ pw = getpwnam(name);
+ if (!pw) bb_error_msg_and_die("unknown user %s", name);
+ if (myuid && pw->pw_uid != myuid) {
+ /* LOGMODE_BOTH */
+ bb_error_msg_and_die("%s can't change password for %s", myname, name);
+ }
+
+ filename = bb_path_passwd_file;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ if (getspnam_r(pw->pw_name, &spw, buffer, sizeof(buffer), &result)) {
+ /* LOGMODE_BOTH */
+ bb_error_msg("no record of %s in %s, using %s",
+ name, bb_path_shadow_file,
+ bb_path_passwd_file);
+ } else {
+ filename = bb_path_shadow_file;
+ pw->pw_passwd = spw.sp_pwdp;
+ }
+#endif
+
+ /* 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 '!' */
+ newp = xstrdup(&pw->pw_passwd[1]);
+ } else if (opt & OPT_delete) {
+ newp = xstrdup("");
+ }
+
+ rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * 30000;
+ setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
+ signal(SIGHUP, SIG_IGN);
+ signal(SIGINT, SIG_IGN);
+ signal(SIGQUIT, SIG_IGN);
+ umask(077);
+ xsetuid(0);
+ if (update_passwd(filename, name, newp) != 0) {
+ /* LOGMODE_BOTH */
+ bb_error_msg_and_die("cannot update password file %s",
+ filename);
+ }
+ /* LOGMODE_BOTH */
+ 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/i/pc104/initrd/conf/busybox/loginutils/su.c b/i/pc104/initrd/conf/busybox/loginutils/su.c
new file mode 100644
index 0000000..0a786cb
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/su.c
@@ -0,0 +1,103 @@
+/* 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 "busybox.h"
+#include <syslog.h>
+
+#define SU_OPT_mp (3)
+#define SU_OPT_l (4)
+
+int su_main(int argc, char **argv);
+int su_main(int argc, char **argv)
+{
+ 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(argc, argv, "mplc:s:", &opt_command, &opt_shell);
+ argc -= optind;
+ argv += optind;
+
+ if (argc && LONE_DASH(argv[0])) {
+ flags |= SU_OPT_l;
+ argc--;
+ argv++;
+ }
+
+ /* get user if specified */
+ if (argc) {
+ opt_username = argv[0];
+// argc--;
+ argv++;
+ }
+
+ 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 = ttyname(2) ? : "none";
+ openlog(applet_name, 0, LOG_AUTH);
+ }
+
+ pw = getpwnam(opt_username);
+ if (!pw)
+ bb_error_msg_and_die("unknown id: %s", 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;
+
+ 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 && (flags & SU_OPT_mp))
+ opt_shell = getenv("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. */
+ bb_error_msg("using restricted shell");
+ opt_shell = 0;
+ }
+#endif
+ if (!opt_shell)
+ opt_shell = pw->pw_shell;
+
+ change_identity(pw);
+ setup_environment(opt_shell, flags & SU_OPT_l, !(flags & SU_OPT_mp), pw);
+ USE_SELINUX(set_current_security_context(NULL);)
+
+ /* Never returns */
+ run_shell(opt_shell, flags & SU_OPT_l, opt_command, (const char**)argv);
+
+ return EXIT_FAILURE;
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/sulogin.c b/i/pc104/initrd/conf/busybox/loginutils/sulogin.c
new file mode 100644
index 0000000..38503f7
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/sulogin.c
@@ -0,0 +1,126 @@
+/* vi: set sw=4 ts=4: */
+/*
+ * Mini sulogin implementation for busybox
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+#include <syslog.h>
+
+#include "busybox.h"
+
+static const char * const 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 ATTRIBUTE_UNUSED junk)
+{
+ exit(EXIT_FAILURE);
+}
+
+
+int sulogin_main(int argc, char **argv);
+int sulogin_main(int argc, char **argv)
+{
+ char *cp;
+ int timeout = 0;
+ char *timeout_arg;
+ const char * const *p;
+ struct passwd *pwd;
+ const char *shell;
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ /* Using _r function to avoid pulling in static buffers */
+ char buffer[256];
+ struct spwd spw;
+ struct spwd *result;
+#endif
+
+ logmode = LOGMODE_BOTH;
+ openlog(applet_name, 0, LOG_AUTH);
+
+ if (getopt32(argc, argv, "t:", &timeout_arg)) {
+ timeout = xatoi_u(timeout_arg);
+ }
+
+ if (argv[optind]) {
+ close(0);
+ close(1);
+ dup(xopen(argv[optind], O_RDWR));
+ close(2);
+ dup(0);
+ }
+
+ if (!isatty(0) || !isatty(1) || !isatty(2)) {
+ logmode = LOGMODE_SYSLOG;
+ bb_error_msg_and_die("not a tty");
+ }
+
+ /* Clear out anything dangerous from the environment */
+ for (p = forbid; *p; p++)
+ unsetenv(*p);
+
+ signal(SIGALRM, catchalarm);
+
+ pwd = getpwuid(0);
+ if (!pwd) {
+ goto auth_error;
+ }
+
+#if ENABLE_FEATURE_SHADOWPASSWDS
+ if (getspnam_r(pwd->pw_name, &spw, buffer, sizeof(buffer), &result)) {
+ goto auth_error;
+ }
+ pwd->pw_passwd = spw.sp_pwdp;
+#endif
+
+ while (1) {
+ /* cp points to a static buffer that is zeroed every time */
+ cp = bb_askpass(timeout,
+ "Give root password for system maintenance\n"
+ "(or type Control-D for normal startup):");
+
+ if (!cp || !*cp) {
+ bb_info_msg("Normal startup");
+ return 0;
+ }
+ if (strcmp(pw_encrypt(cp, pwd->pw_passwd), pwd->pw_passwd) == 0) {
+ break;
+ }
+ bb_do_delay(FAIL_DELAY);
+ bb_error_msg("login incorrect");
+ }
+ 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;
+ }
+ run_shell(shell, 1, 0, 0);
+ /* never returns */
+
+auth_error:
+ bb_error_msg_and_die("no password entry for 'root'");
+}
diff --git a/i/pc104/initrd/conf/busybox/loginutils/vlock.c b/i/pc104/initrd/conf/busybox/loginutils/vlock.c
new file mode 100644
index 0000000..06a7169
--- /dev/null
+++ b/i/pc104/initrd/conf/busybox/loginutils/vlock.c
@@ -0,0 +1,119 @@
+/* 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>
+ *
+ * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
+ */
+
+/* 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 "busybox.h"
+#include <sys/vt.h>
+
+static struct passwd *pw;
+static struct vt_mode ovtm;
+static struct termios oterm;
+static int vfd;
+static unsigned long o_lock_all;
+
+static void release_vt(int signo)
+{
+ ioctl(vfd, VT_RELDISP, !o_lock_all);
+}
+
+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);
+}
+
+int vlock_main(int argc, char **argv);
+int vlock_main(int argc, char **argv)
+{
+ sigset_t sig;
+ struct sigaction sa;
+ struct vt_mode vtm;
+ struct termios term;
+ uid_t uid = getuid();
+
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ bb_error_msg_and_die("unknown uid %d", uid);
+
+ if (argc > 2) {
+ bb_show_usage();
+ }
+
+ o_lock_all = getopt32(argc, argv, "a");
+
+ vfd = xopen(CURRENT_TTY, O_RDWR);
+
+ if (ioctl(vfd, VT_GETMODE, &vtm) < 0) {
+ bb_perror_msg_and_die("VT_GETMODE");
+ }
+
+ /* 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 {
+ printf("Virtual Console%s locked by %s.\n", (o_lock_all) ? "s" : "", pw->pw_name);
+ if (correct_password(pw)) {
+ break;
+ }
+ bb_do_delay(FAIL_DELAY);
+ puts("Password incorrect");
+ } while (1);
+ restore_terminal();
+ fflush_stdout_and_exit(0);
+}