summaryrefslogtreecommitdiff
path: root/busybox/loginutils
diff options
context:
space:
mode:
authornobody <nobody@localhost>2004-10-13 09:42:10 +0000
committernobody <nobody@localhost>2004-10-13 09:42:10 +0000
commit8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373 (patch)
tree1826706cd4fd009fcd14f4f8021005ec8ec0fa59 /busybox/loginutils
downloadbusybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.tar.gz
busybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.tar.bz2
busybox-w32-8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373.zip
This commit was manufactured by cvs2svn to create tag 'busybox_1_00'.
Diffstat (limited to 'busybox/loginutils')
-rw-r--r--busybox/loginutils/Config.in161
-rw-r--r--busybox/loginutils/Makefile32
-rw-r--r--busybox/loginutils/Makefile.in57
-rw-r--r--busybox/loginutils/addgroup.c171
-rw-r--r--busybox/loginutils/adduser.c314
-rw-r--r--busybox/loginutils/delgroup.c62
-rw-r--r--busybox/loginutils/delline.c113
-rw-r--r--busybox/loginutils/deluser.c68
-rw-r--r--busybox/loginutils/getty.c1020
-rw-r--r--busybox/loginutils/login.c486
-rw-r--r--busybox/loginutils/passwd.c400
-rw-r--r--busybox/loginutils/su.c157
-rw-r--r--busybox/loginutils/sulogin.c158
-rw-r--r--busybox/loginutils/vlock.c231
14 files changed, 3430 insertions, 0 deletions
diff --git a/busybox/loginutils/Config.in b/busybox/loginutils/Config.in
new file mode 100644
index 000000000..5619aa9af
--- /dev/null
+++ b/busybox/loginutils/Config.in
@@ -0,0 +1,161 @@
1#
2# For a description of the syntax of this configuration file,
3# see scripts/kbuild/config-language.txt.
4#
5
6menu "Login/Password Management Utilities"
7
8config CONFIG_USE_BB_PWD_GRP
9 bool "Use internal password and group functions rather than system functions"
10 default n
11 help
12 If you leave this disabled, busybox will use the system's password
13 and group functions. And if you are using the GNU C library
14 (glibc), you will then need to install the /etc/nsswitch.conf
15 configuration file and the required /lib/libnss_* libraries in
16 order for the password and group functions to work. This generally
17 makes your embedded system quite a bit larger.
18
19 Enabling this option will cause busybox to directly access the
20 system's /etc/password, /etc/group files (and your system will be
21 smaller, and I will get fewer emails asking about how glibc NSS
22 works). When this option is enabled, you will not be able to use
23 PAM to access remote LDAP password servers and whatnot. And if you
24 want hostname resolution to work with glibc, you still need the
25 /lib/libnss_* libraries.
26
27 If you enable this option, it will add about 1.5k to busybox.
28
29
30config CONFIG_ADDGROUP
31 bool "addgroup"
32 default n
33 help
34 Utility for creating a new group account.
35
36config CONFIG_DELGROUP
37 bool "delgroup"
38 default n
39 help
40 Utility for deleting a group account.
41
42config CONFIG_ADDUSER
43 bool "adduser"
44 default n
45 help
46 Utility for creating a new user account.
47
48config CONFIG_DELUSER
49 bool "deluser"
50 default n
51 help
52 Utility for deleting a user account.
53
54config CONFIG_GETTY
55 bool "getty"
56 default n
57 help
58 getty lets you log in on a tty, it is normally invoked by init.
59
60config CONFIG_FEATURE_U_W_TMP
61 bool " Support utmp and wtmp files"
62 depends on CONFIG_GETTY || CONFIG_LOGIN || CONFIG_SU || CONFIG_WHO || CONFIG_LAST
63 default n
64 help
65 The files /var/run/utmp and /var/run/wtmp can be used to track when
66 user's have logged into and logged out of the system, allowing programs
67 such as 'who' and 'last' to list who is currently logged in.
68
69config CONFIG_LOGIN
70 bool "login"
71 default n
72 select CONFIG_FEATURE_SUID
73 help
74 login is used when signing onto a system.
75
76 Note that Busybox binary must be setuid root for this applet to
77 work properly.
78
79config CONFIG_FEATURE_SECURETTY
80 bool " Support for /etc/securetty"
81 default y
82 depends on CONFIG_LOGIN
83 help
84 The file /etc/securetty is used by (some versions of) login(1).
85 The file contains the device names of tty lines (one per line,
86 without leading /dev/) on which root is allowed to login.
87
88config CONFIG_PASSWD
89 bool "passwd"
90 default n
91 select CONFIG_FEATURE_SUID
92 help
93 passwd changes passwords for user and group accounts. A normal user
94 may only change the password for his/her own account, the super user
95 may change the password for any account. The administrator of a group
96 may change the password for the group.
97
98 Note that Busybox binary must be setuid root for this applet to
99 work properly.
100
101config CONFIG_SU
102 bool "su"
103 default n
104 select CONFIG_FEATURE_SUID
105 help
106 su is used to become another user during a login session.
107 Invoked without a username, su defaults to becoming the super user.
108
109 Note that Busybox binary must be setuid root for this applet to
110 work properly.
111
112config CONFIG_SULOGIN
113 bool "sulogin"
114 default n
115 help
116 sulogin is invoked when the system goes into single user
117 mode (this is done through an entry in inittab).
118
119config CONFIG_VLOCK
120 bool "vlock"
121 default n
122 select CONFIG_FEATURE_SUID
123 help
124 Build the "vlock" applet which allows you to lock (virtual) terminals.
125
126 Note that Busybox binary must be setuid root for this applet to
127 work properly.
128
129comment "Common options for adduser, deluser, login, su"
130 depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU
131
132config CONFIG_FEATURE_SHADOWPASSWDS
133 bool "Support for shadow passwords"
134 default n
135 depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU
136 help
137 Build support for shadow password in /etc/shadow. This file is only
138 readable by root and thus the encrypted passwords are no longer
139 publicly readable.
140
141config CONFIG_USE_BB_SHADOW
142 bool " Use busybox shadow password functions"
143 default n
144 depends on CONFIG_USE_BB_PWD_GRP && CONFIG_FEATURE_SHADOWPASSWDS
145 help
146 If you leave this disabled, busybox will use the system's shadow
147 password handling functions. And if you are using the GNU C library
148 (glibc), you will then need to install the /etc/nsswitch.conf
149 configuration file and the required /lib/libnss_* libraries in
150 order for the shadow password functions to work. This generally
151 makes your embedded system quite a bit larger.
152
153 Enabling this option will cause busybox to directly access the
154 system's /etc/shadow file when handling shadow passwords. This
155 makes your system smaller and I will get fewer emails asking about
156 how glibc NSS works). When this option is enabled, you will not be
157 able to use PAM to access shadow passwords from remote LDAP
158 password servers and whatnot.
159
160endmenu
161
diff --git a/busybox/loginutils/Makefile b/busybox/loginutils/Makefile
new file mode 100644
index 000000000..98226ae81
--- /dev/null
+++ b/busybox/loginutils/Makefile
@@ -0,0 +1,32 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20top_srcdir=..
21top_builddir=..
22srcdir=$(top_srcdir)/loginutils
23LOGINUTILS_DIR:=./
24include $(top_builddir)/Rules.mak
25include $(top_builddir)/.config
26include Makefile.in
27all: $(libraries-y)
28-include $(top_builddir)/.depend
29
30clean:
31 rm -f *.o *.a $(AR_TARGET)
32
diff --git a/busybox/loginutils/Makefile.in b/busybox/loginutils/Makefile.in
new file mode 100644
index 000000000..96a61e60f
--- /dev/null
+++ b/busybox/loginutils/Makefile.in
@@ -0,0 +1,57 @@
1# Makefile for busybox
2#
3# Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
4#
5# This program is free software; you can redistribute it and/or modify
6# it under the terms of the GNU General Public License as published by
7# the Free Software Foundation; either version 2 of the License, or
8# (at your option) any later version.
9#
10# This program is distributed in the hope that it will be useful,
11# but WITHOUT ANY WARRANTY; without even the implied warranty of
12# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13# General Public License for more details.
14#
15# You should have received a copy of the GNU General Public License
16# along with this program; if not, write to the Free Software
17# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18#
19
20LOGINUTILS_AR:=loginutils.a
21ifndef LOGINUTILS_DIR
22LOGINUTILS_DIR:=$(top_builddir)/loginutils/
23endif
24srcdir=$(top_srcdir)/loginutils
25
26LOGINUTILS-y:=
27LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o
28LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o
29LOGINUTILS-$(CONFIG_GETTY) += getty.o
30LOGINUTILS-$(CONFIG_LOGIN) += login.o
31LOGINUTILS-$(CONFIG_PASSWD) += passwd.o
32LOGINUTILS-$(CONFIG_SU) += su.o
33LOGINUTILS-$(CONFIG_SULOGIN) += sulogin.o
34LOGINUTILS-$(CONFIG_VLOCK) += vlock.o
35LOGINUTILS-$(CONFIG_DELUSER) += deluser.o
36LOGINUTILS-$(CONFIG_DELGROUP) += delgroup.o
37
38libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR)
39
40needcrypt-y:=
41needcrypt-$(CONFIG_LOGIN) := y
42needcrypt-$(CONFIG_PASSWD) := y
43needcrypt-$(CONFIG_SU) := y
44needcrypt-$(CONFIG_SULOGIN) := y
45needcrypt-$(CONFIG_VLOCK) := y
46
47
48ifeq ($(needcrypt-y),y)
49 LIBRARIES += -lcrypt
50endif
51
52$(LOGINUTILS_DIR)$(LOGINUTILS_AR): $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y))
53 $(AR) -ro $@ $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y))
54
55$(LOGINUTILS_DIR)%.o: $(srcdir)/%.c
56 $(CC) $(CFLAGS) $(EXTRA_CFLAGS) -c -o $@ $<
57
diff --git a/busybox/loginutils/addgroup.c b/busybox/loginutils/addgroup.c
new file mode 100644
index 000000000..804d6961c
--- /dev/null
+++ b/busybox/loginutils/addgroup.c
@@ -0,0 +1,171 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * addgroup - add users to /etc/passwd and /etc/shadow
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <errno.h>
25#include <fcntl.h>
26#include <stdarg.h>
27#include <stdio.h>
28#include <stdlib.h>
29#include <string.h>
30#include <sys/param.h>
31#include <sys/stat.h>
32#include <sys/types.h>
33#include <unistd.h>
34#include "busybox.h"
35#include "pwd_.h"
36#include "grp_.h"
37
38
39/* structs __________________________ */
40
41/* data _____________________________ */
42
43/* defaults : should this be in an external file? */
44static const char default_passwd[] = "x";
45
46
47/* make sure gr_name isn't taken, make sure gid is kosher
48 * return 1 on failure */
49static int group_study(const char *filename, struct group *g)
50{
51 FILE *etc_group;
52 gid_t desired;
53
54 struct group *grp;
55 const int max = 65000;
56
57 etc_group = bb_xfopen(filename, "r");
58
59 /* make sure gr_name isn't taken, make sure gid is kosher */
60 desired = g->gr_gid;
61 while ((grp = fgetgrent(etc_group))) {
62 if ((strcmp(grp->gr_name, g->gr_name)) == 0) {
63 bb_error_msg_and_die("%s: group already in use\n", g->gr_name);
64 }
65 if ((desired) && grp->gr_gid == desired) {
66 bb_error_msg_and_die("%d: gid has already been allocated\n",
67 desired);
68 }
69 if ((grp->gr_gid > g->gr_gid) && (grp->gr_gid < max)) {
70 g->gr_gid = grp->gr_gid;
71 }
72 }
73 fclose(etc_group);
74
75 /* gid */
76 if (desired) {
77 g->gr_gid = desired;
78 } else {
79 g->gr_gid++;
80 }
81 /* return 1; */
82 return 0;
83}
84
85/* append a new user to the passwd file */
86static int addgroup(const char *filename, char *group, gid_t gid, const char *user)
87{
88 FILE *etc_group;
89
90#ifdef CONFIG_FEATURE_SHADOWPASSWDS
91 FILE *etc_gshadow;
92#endif
93
94 struct group gr;
95
96 /* group:passwd:gid:userlist */
97 static const char entryfmt[] = "%s:%s:%d:%s\n";
98
99 /* make sure gid and group haven't already been allocated */
100 gr.gr_gid = gid;
101 gr.gr_name = group;
102 if (group_study(filename, &gr))
103 return 1;
104
105 /* add entry to group */
106 etc_group = bb_xfopen(filename, "a");
107
108 fprintf(etc_group, entryfmt, group, default_passwd, gr.gr_gid, user);
109 fclose(etc_group);
110
111
112#ifdef CONFIG_FEATURE_SHADOWPASSWDS
113 /* add entry to gshadow if necessary */
114 if (access(bb_path_gshadow_file, F_OK|W_OK) == 0) {
115 etc_gshadow = bb_xfopen(bb_path_gshadow_file, "a");
116 fprintf(etc_gshadow, "%s:!::\n", group);
117 fclose(etc_gshadow);
118 }
119#endif
120
121 /* return 1; */
122 return 0;
123}
124
125#ifndef CONFIG_ADDUSER
126static inline void if_i_am_not_root(void)
127{
128 if (geteuid()) {
129 bb_error_msg_and_die( "Only root may add a user or group to the system.");
130 }
131}
132#else
133extern void if_i_am_not_root(void);
134#endif
135
136/*
137 * addgroup will take a login_name as its first parameter.
138 *
139 * gid
140 *
141 * can be customized via command-line parameters.
142 * ________________________________________________________________________ */
143int addgroup_main(int argc, char **argv)
144{
145 char *group;
146 char *user;
147 gid_t gid = 0;
148
149 /* get remaining args */
150 if(bb_getopt_ulflags(argc, argv, "g:", &group)) {
151 gid = bb_xgetlarg(group, 10, 0, LONG_MAX);
152 }
153
154 if (optind < argc) {
155 group = argv[optind];
156 optind++;
157 } else {
158 bb_show_usage();
159 }
160
161 if (optind < argc) {
162 user = argv[optind];
163 } else {
164 user = "";
165 }
166
167 if_i_am_not_root();
168
169 /* werk */
170 return addgroup(bb_path_group_file, group, gid, user);
171}
diff --git a/busybox/loginutils/adduser.c b/busybox/loginutils/adduser.c
new file mode 100644
index 000000000..7fa05a013
--- /dev/null
+++ b/busybox/loginutils/adduser.c
@@ -0,0 +1,314 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * adduser - add users to /etc/passwd and /etc/shadow
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#ifndef _GNU_SOURCE
25#define _GNU_SOURCE
26#endif
27#include <errno.h>
28#include <fcntl.h>
29#include <stdarg.h>
30#include <stdio.h>
31#include <stdlib.h>
32#include <string.h>
33#include <time.h>
34#include <unistd.h>
35#include <getopt.h>
36#include <sys/param.h>
37#include <sys/stat.h>
38#include <sys/types.h>
39#include "busybox.h"
40
41
42
43/* structs __________________________ */
44
45typedef struct {
46 uid_t u;
47 gid_t g;
48} Id;
49
50/* data _____________________________ */
51
52/* defaults : should this be in an external file? */
53static const char default_passwd[] = "x";
54static const char default_gecos[] = "Linux User,,,";
55static const char default_home_prefix[] = "/home";
56
57#ifdef CONFIG_FEATURE_SHADOWPASSWDS
58/* shadow in use? */
59static int shadow_enabled = 0;
60#endif
61
62/* remix */
63/* EDR recoded such that the uid may be passed in *p */
64static int passwd_study(const char *filename, struct passwd *p)
65{
66 struct passwd *pw;
67 FILE *passwd;
68
69 const int min = 500;
70 const int max = 65000;
71
72 passwd = bb_wfopen(filename, "r");
73 if (!passwd)
74 return 4;
75
76 /* EDR if uid is out of bounds, set to min */
77 if ((p->pw_uid > max) || (p->pw_uid < min))
78 p->pw_uid = min;
79
80 /* stuff to do:
81 * make sure login isn't taken;
82 * find free uid and gid;
83 */
84 while ((pw = fgetpwent(passwd))) {
85 if (strcmp(pw->pw_name, p->pw_name) == 0) {
86 /* return 0; */
87 return 1;
88 }
89 if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max)
90 && (pw->pw_uid >= min)) {
91 p->pw_uid = pw->pw_uid + 1;
92 }
93 }
94
95 if (p->pw_gid == 0) {
96 /* EDR check for an already existing gid */
97 while (getgrgid(p->pw_uid) != NULL)
98 p->pw_uid++;
99
100 /* EDR also check for an existing group definition */
101 if (getgrnam(p->pw_name) != NULL)
102 return 3;
103
104 /* EDR create new gid always = uid */
105 p->pw_gid = p->pw_uid;
106 }
107
108 /* EDR bounds check */
109 if ((p->pw_uid > max) || (p->pw_uid < min))
110 return 2;
111
112 /* return 1; */
113 return 0;
114}
115
116static void addgroup_wrapper(const char *login, gid_t gid)
117{
118 char *cmd;
119
120 bb_xasprintf(&cmd, "addgroup -g %d %s", gid, login);
121 system(cmd);
122 free(cmd);
123}
124
125static void passwd_wrapper(const char *login) __attribute__ ((noreturn));
126
127static void passwd_wrapper(const char *login)
128{
129 static const char prog[] = "passwd";
130 execlp(prog, prog, login, NULL);
131 bb_error_msg_and_die("Failed to execute '%s', you must set the password for '%s' manually", prog, login);
132}
133
134/* putpwent(3) remix */
135static int adduser(const char *filename, struct passwd *p, int makehome, int setpass)
136{
137 FILE *passwd;
138 int r;
139#ifdef CONFIG_FEATURE_SHADOWPASSWDS
140 FILE *shadow;
141 struct spwd *sp;
142#endif
143 int new_group = 1;
144
145 /* if using a pre-existing group, don't create one */
146 if (p->pw_gid != 0)
147 new_group = 0;
148
149 /* make sure everything is kosher and setup uid && gid */
150 passwd = bb_wfopen(filename, "a");
151 if (passwd == NULL) {
152 return 1;
153 }
154 fseek(passwd, 0, SEEK_END);
155
156 /* if (passwd_study(filename, p) == 0) { */
157 r = passwd_study(filename, p);
158 if (r) {
159 if (r == 1)
160 bb_error_msg("%s: login already in use", p->pw_name);
161 else if (r == 2)
162 bb_error_msg("illegal uid or no uids left");
163 else if (r == 3)
164 bb_error_msg("group name %s already in use", p->pw_name);
165 else
166 bb_error_msg("generic error.");
167 return 1;
168 }
169
170 /* add to passwd */
171 if (putpwent(p, passwd) == -1) {
172 return 1;
173 }
174 fclose(passwd);
175
176#ifdef CONFIG_FEATURE_SHADOWPASSWDS
177 /* add to shadow if necessary */
178 if (shadow_enabled) {
179 shadow = bb_wfopen(bb_path_shadow_file, "a");
180 if (shadow == NULL) {
181 return 1;
182 }
183 fseek(shadow, 0, SEEK_END);
184 sp = pwd_to_spwd(p);
185 sp->sp_max = 99999; /* debianish */
186 sp->sp_warn = 7;
187 fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n",
188 sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max,
189 sp->sp_warn);
190 fclose(shadow);
191 }
192#endif
193
194 if (new_group) {
195 /* add to group */
196 /* addgroup should be responsible for dealing w/ gshadow */
197 addgroup_wrapper(p->pw_name, p->pw_gid);
198 }
199
200 /* Clear the umask for this process so it doesn't
201 * * screw up the permissions on the mkdir and chown. */
202 umask(0);
203
204 if (makehome) {
205 /* mkdir */
206 if (mkdir(p->pw_dir, 0755)) {
207 bb_perror_msg("%s", p->pw_dir);
208 }
209 /* Set the owner and group so it is owned by the new user. */
210 if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) {
211 bb_perror_msg("%s", p->pw_dir);
212 }
213 /* Now fix up the permissions to 2755. Can't do it before now
214 * since chown will clear the setgid bit */
215 if (chmod(p->pw_dir, 02755)) {
216 bb_perror_msg("%s", p->pw_dir);
217 }
218 }
219
220 if (setpass) {
221 /* interactively set passwd */
222 passwd_wrapper(p->pw_name);
223 }
224
225 return 0;
226}
227
228
229/* return current uid (root is always uid == 0, right?) */
230#ifndef CONFIG_ADDGROUP
231static inline void if_i_am_not_root(void)
232#else
233void if_i_am_not_root(void)
234#endif
235{
236 if (geteuid()) {
237 bb_error_msg_and_die( "Only root may add a user or group to the system.");
238 }
239}
240
241#define SETPASS (1 << 4)
242#define MAKEHOME (1 << 6)
243
244/*
245 * adduser will take a login_name as its first parameter.
246 *
247 * home
248 * shell
249 * gecos
250 *
251 * can be customized via command-line parameters.
252 * ________________________________________________________________________ */
253int adduser_main(int argc, char **argv)
254{
255 struct passwd pw;
256 const char *login;
257 const char *gecos = default_gecos;
258 const char *home = NULL;
259 const char *shell = DEFAULT_SHELL;
260 const char *usegroup = NULL;
261 int flags;
262 int setpass = 1;
263 int makehome = 1;
264
265 /* init */
266 if (argc < 2) {
267 bb_show_usage();
268 }
269 /* get args */
270 flags = bb_getopt_ulflags(argc, argv, "h:g:s:G:DSH", &home, &gecos, &shell, &usegroup);
271
272 if (flags & SETPASS) {
273 setpass = 0;
274 }
275 if (flags & MAKEHOME) {
276 makehome = 0;
277 }
278
279 /* got root? */
280 if_i_am_not_root();
281
282 /* get login */
283 if (optind >= argc) {
284 bb_error_msg_and_die( "no user specified");
285 }
286 login = argv[optind];
287
288 /* create string for $HOME if not specified already */
289 if (!home) {
290 home = concat_path_file(default_home_prefix, login);
291 }
292#ifdef CONFIG_FEATURE_SHADOWPASSWDS
293 /* is /etc/shadow in use? */
294 shadow_enabled = (0 == access(bb_path_shadow_file, F_OK));
295#endif
296
297 /* create a passwd struct */
298 pw.pw_name = (char *)login;
299 pw.pw_passwd = (char *)default_passwd;
300 pw.pw_uid = 0;
301 pw.pw_gid = 0;
302 pw.pw_gecos = (char *)gecos;
303 pw.pw_dir = (char *)home;
304 pw.pw_shell = (char *)shell;
305
306 if (usegroup) {
307 /* Add user to a group that already exists */
308 pw.pw_gid = my_getgrnam(usegroup);
309 /* exits on error */
310 }
311
312 /* grand finale */
313 return adduser(bb_path_passwd_file, &pw, makehome, setpass);
314}
diff --git a/busybox/loginutils/delgroup.c b/busybox/loginutils/delgroup.c
new file mode 100644
index 000000000..91edf2989
--- /dev/null
+++ b/busybox/loginutils/delgroup.c
@@ -0,0 +1,62 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * deluser (remove lusers from the system ;) for TinyLogin
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <sys/stat.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include "busybox.h"
30
31
32#if ! defined CONFIG_DELUSER
33#include "delline.c"
34#else
35extern int del_line_matching(const char *login, const char *filename);
36#endif
37
38int delgroup_main(int argc, char **argv)
39{
40 /* int successful; */
41 int failure;
42
43 if (argc != 2) {
44 bb_show_usage();
45 } else {
46
47 failure = del_line_matching(argv[1], bb_path_group_file);
48#ifdef CONFIG_FEATURE_SHADOWPASSWDS
49 if (access(bb_path_gshadow_file, W_OK) == 0) {
50 /* EDR the |= works if the error is not 0, so he had it wrong */
51 failure |= del_line_matching(argv[1], bb_path_gshadow_file);
52 }
53#endif
54 if (failure) {
55 bb_error_msg_and_die("%s: Group could not be removed\n", argv[1]);
56 }
57
58 }
59 return (EXIT_SUCCESS);
60}
61
62/* $Id: delgroup.c,v 1.2 2003/07/14 21:50:51 andersen Exp $ */
diff --git a/busybox/loginutils/delline.c b/busybox/loginutils/delline.c
new file mode 100644
index 000000000..8d534c861
--- /dev/null
+++ b/busybox/loginutils/delline.c
@@ -0,0 +1,113 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * deluser (remove lusers from the system ;) for TinyLogin
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <sys/stat.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include "busybox.h"
30
31
32
33/* where to start and stop deletion */
34typedef struct {
35 size_t start;
36 size_t stop;
37} Bounds;
38
39/* An interesting side-effect of boundary()'s
40 * implementation is that the first user (typically root)
41 * cannot be removed. Let's call it a feature. */
42static inline Bounds boundary(const char *buffer, const char *login)
43{
44 char needle[256];
45 char *start;
46 char *stop;
47 Bounds b;
48
49 snprintf(needle, 256, "\n%s:", login);
50 needle[255] = 0;
51 start = strstr(buffer, needle);
52 if (!start) {
53 b.start = 0;
54 b.stop = 0;
55 return b;
56 }
57 start++;
58
59 stop = index(start, '\n'); /* index is a BSD-ism */
60 b.start = start - buffer;
61 b.stop = stop - buffer;
62 return b;
63}
64
65/* grep -v ^login (except it only deletes the first match) */
66/* ...in fact, I think I'm going to simplify this later */
67int del_line_matching(const char *login, const char *filename)
68{
69 char *buffer;
70 FILE *passwd;
71 size_t len;
72 Bounds b;
73 struct stat statbuf;
74
75 /* load into buffer */
76 passwd = fopen(filename, "r");
77 if (!passwd) {
78 return 1;
79 }
80 stat(filename, &statbuf);
81 len = statbuf.st_size;
82 buffer = (char *) malloc(len * sizeof(char));
83
84 if (!buffer) {
85 fclose(passwd);
86 return 1;
87 }
88 fread(buffer, len, sizeof(char), passwd);
89
90 fclose(passwd);
91
92 /* find the user to remove */
93 b = boundary(buffer, login);
94 if (b.stop == 0) {
95 free(buffer);
96 return 1;
97 }
98
99 /* write the file w/o the user */
100 passwd = fopen(filename, "w");
101 if (!passwd) {
102 return 1;
103 }
104 fwrite(buffer, (b.start - 1), sizeof(char), passwd);
105 fwrite(&buffer[b.stop], (len - b.stop), sizeof(char), passwd);
106
107 fclose(passwd);
108
109 return 0;
110}
111
112
113/* $Id: delline.c,v 1.2 2003/07/14 21:50:51 andersen Exp $ */
diff --git a/busybox/loginutils/deluser.c b/busybox/loginutils/deluser.c
new file mode 100644
index 000000000..1cd2b01e3
--- /dev/null
+++ b/busybox/loginutils/deluser.c
@@ -0,0 +1,68 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * deluser (remove lusers from the system ;) for TinyLogin
4 *
5 * Copyright (C) 1999 by Lineo, inc. and John Beppu
6 * Copyright (C) 1999,2000,2001 by John Beppu <beppu@codepoet.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24#include <sys/stat.h>
25#include <unistd.h>
26#include <stdio.h>
27#include <stdlib.h>
28#include <string.h>
29#include "busybox.h"
30
31
32#include "delline.c"
33
34static const char deluser_format[]="%s: User could not be removed from %s";
35
36int deluser_main(int argc, char **argv)
37{
38 /* int successful; */
39 int failure;
40
41 if (argc != 2) {
42 bb_show_usage();
43 } else {
44
45 failure = del_line_matching(argv[1], bb_path_passwd_file);
46 if (failure) {
47 bb_error_msg_and_die(deluser_format, argv[1], bb_path_passwd_file);
48 }
49#ifdef CONFIG_FEATURE_SHADOWPASSWDS
50 failure = del_line_matching(argv[1], bb_path_shadow_file);
51 if (failure) {
52 bb_error_msg_and_die(deluser_format, argv[1], bb_path_shadow_file);
53 }
54 failure = del_line_matching(argv[1], bb_path_gshadow_file);
55 if (failure) {
56 bb_error_msg_and_die(deluser_format, argv[1], bb_path_gshadow_file);
57 }
58#endif
59 failure = del_line_matching(argv[1], bb_path_group_file);
60 if (failure) {
61 bb_error_msg_and_die(deluser_format, argv[1], bb_path_group_file);
62 }
63
64 }
65 return (EXIT_SUCCESS);
66}
67
68/* $Id: deluser.c,v 1.4 2003/07/14 20:20:45 andersen Exp $ */
diff --git a/busybox/loginutils/getty.c b/busybox/loginutils/getty.c
new file mode 100644
index 000000000..923432ba1
--- /dev/null
+++ b/busybox/loginutils/getty.c
@@ -0,0 +1,1020 @@
1/* vi: set sw=4 ts=4: */
2/* agetty.c - another getty program for Linux. By W. Z. Venema 1989
3 Ported to Linux by Peter Orbaek <poe@daimi.aau.dk>
4 This program is freely distributable. The entire man-page used to
5 be here. Now read the real man-page agetty.8 instead.
6
7 -f option added by Eric Rasmussen <ear@usfirst.org> - 12/28/95
8
9 1999-02-22 Arkadiusz Mi¶kiewicz <misiek@misiek.eu.org>
10 - added Native Language Support
11
12 1999-05-05 Thorsten Kranzkowski <dl8bcu@gmx.net>
13 - enable hardware flow control before displaying /etc/issue
14
15*/
16
17#include <stdio.h>
18#include <stdlib.h>
19#include <unistd.h>
20#include <string.h>
21#include <sys/ioctl.h>
22#include <errno.h>
23#include <sys/stat.h>
24#include <sys/signal.h>
25#include <fcntl.h>
26#include <stdarg.h>
27#include <ctype.h>
28#include <utmp.h>
29#include <getopt.h>
30#include <termios.h>
31#include "busybox.h"
32
33#define _PATH_LOGIN "/bin/login"
34
35 /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */
36#ifdef CONFIG_SYSLOGD
37#include <sys/param.h>
38#define USE_SYSLOG
39#include <syslog.h>
40#endif
41
42
43 /*
44 * Some heuristics to find out what environment we are in: if it is not
45 * System V, assume it is SunOS 4.
46 */
47
48#ifdef LOGIN_PROCESS /* defined in System V utmp.h */
49#define SYSV_STYLE /* select System V style getty */
50#ifdef CONFIG_FEATURE_U_W_TMP
51extern void updwtmp(const char *filename, const struct utmp *ut);
52#endif
53#endif /* LOGIN_PROCESS */
54
55 /*
56 * Things you may want to modify.
57 *
58 * If ISSUE is not defined, agetty will never display the contents of the
59 * /etc/issue file. You will not want to spit out large "issue" files at the
60 * wrong baud rate. Relevant for System V only.
61 *
62 * You may disagree with the default line-editing etc. characters defined
63 * below. Note, however, that DEL cannot be used for interrupt generation
64 * and for line editing at the same time.
65 */
66
67#ifdef SYSV_STYLE
68#define ISSUE "/etc/issue" /* displayed before the login prompt */
69#include <sys/utsname.h>
70#include <time.h>
71#endif
72
73/* Some shorthands for control characters. */
74
75#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */
76#define CR CTL('M') /* carriage return */
77#define NL CTL('J') /* line feed */
78#define BS CTL('H') /* back space */
79#define DEL CTL('?') /* delete */
80
81/* Defaults for line-editing etc. characters; you may want to change this. */
82
83#define DEF_ERASE DEL /* default erase character */
84#define DEF_INTR CTL('C') /* default interrupt character */
85#define DEF_QUIT CTL('\\') /* default quit char */
86#define DEF_KILL CTL('U') /* default kill char */
87#define DEF_EOF CTL('D') /* default EOF char */
88#define DEF_EOL 0
89#define DEF_SWITCH 0 /* default switch char */
90
91 /*
92 * SunOS 4.1.1 termio is broken. We must use the termios stuff instead,
93 * because the termio -> termios translation does not clear the termios
94 * CIBAUD bits. Therefore, the tty driver would sometimes report that input
95 * baud rate != output baud rate. I did not notice that problem with SunOS
96 * 4.1. We will use termios where available, and termio otherwise.
97 */
98
99/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set
100 properly, but all is well if we use termios?! */
101
102#ifdef TCGETS
103#undef TCGETA
104#undef TCSETA
105#undef TCSETAW
106#define termio termios
107#define TCGETA TCGETS
108#define TCSETA TCSETS
109#define TCSETAW TCSETSW
110#endif
111
112 /*
113 * This program tries to not use the standard-i/o library. This keeps the
114 * executable small on systems that do not have shared libraries (System V
115 * Release <3).
116 */
117#ifndef BUFSIZ
118#define BUFSIZ 1024
119#endif
120
121 /*
122 * When multiple baud rates are specified on the command line, the first one
123 * we will try is the first one specified.
124 */
125
126#define FIRST_SPEED 0
127
128/* Storage for command-line options. */
129
130#define MAX_SPEED 10 /* max. nr. of baud rates */
131
132struct options {
133 int flags; /* toggle switches, see below */
134 int timeout; /* time-out period */
135 char *login; /* login program */
136 char *tty; /* name of tty */
137 char *initstring; /* modem init string */
138 char *issue; /* alternative issue file */
139 int numspeed; /* number of baud rates to try */
140 int speeds[MAX_SPEED]; /* baud rates to be tried */
141};
142
143#define F_PARSE (1<<0) /* process modem status messages */
144#define F_ISSUE (1<<1) /* display /etc/issue */
145#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */
146#define F_LOCAL (1<<3) /* force local */
147#define F_INITSTRING (1<<4) /* initstring is set */
148#define F_WAITCRLF (1<<5) /* wait for CR or LF */
149#define F_CUSTISSUE (1<<6) /* give alternative issue file */
150#define F_NOPROMPT (1<<7) /* don't ask for login name! */
151
152/* Storage for things detected while the login name was read. */
153
154struct chardata {
155 int erase; /* erase character */
156 int kill; /* kill character */
157 int eol; /* end-of-line character */
158 int parity; /* what parity did we see */
159 int capslock; /* upper case without lower case */
160};
161
162/* Initial values for the above. */
163
164struct chardata init_chardata = {
165 DEF_ERASE, /* default erase character */
166 DEF_KILL, /* default kill character */
167 13, /* default eol char */
168 0, /* space parity */
169 0, /* no capslock */
170};
171
172#if 0
173struct Speedtab {
174 long speed;
175 int code;
176};
177
178static struct Speedtab speedtab[] = {
179 {50, B50},
180 {75, B75},
181 {110, B110},
182 {134, B134},
183 {150, B150},
184 {200, B200},
185 {300, B300},
186 {600, B600},
187 {1200, B1200},
188 {1800, B1800},
189 {2400, B2400},
190 {4800, B4800},
191 {9600, B9600},
192#ifdef B19200
193 {19200, B19200},
194#endif
195#ifdef B38400
196 {38400, B38400},
197#endif
198#ifdef EXTA
199 {19200, EXTA},
200#endif
201#ifdef EXTB
202 {38400, EXTB},
203#endif
204#ifdef B57600
205 {57600, B57600},
206#endif
207#ifdef B115200
208 {115200, B115200},
209#endif
210#ifdef B230400
211 {230400, B230400},
212#endif
213 {0, 0},
214};
215#endif
216
217static void parse_args(int argc, char **argv, struct options *op);
218static void parse_speeds(struct options *op, char *arg);
219static void open_tty(char *tty, struct termio *tp, int local);
220static void termio_init(struct termio *tp, int speed, struct options *op);
221static void auto_baud(struct termio *tp);
222static void do_prompt(struct options *op, struct termio *tp);
223static void next_speed(struct termio *tp, struct options *op);
224static char *get_logname(struct options *op, struct chardata *cp,
225
226 struct termio *tp);
227static void termio_final(struct options *op, struct termio *tp,
228
229 struct chardata *cp);
230static int caps_lock(const char *s);
231static int bcode(char *s);
232static void error(const char *fmt, ...) __attribute__ ((noreturn));
233
234#ifdef CONFIG_FEATURE_U_W_TMP
235static void update_utmp(char *line);
236#endif
237
238/* The following is used for understandable diagnostics. */
239
240/* Fake hostname for ut_host specified on command line. */
241static char *fakehost = NULL;
242
243/* ... */
244#ifdef DEBUGGING
245#define debug(s) fprintf(dbf,s); fflush(dbf)
246#define DEBUGTERM "/dev/ttyp0"
247FILE *dbf;
248#else
249#define debug(s) /* nothing */
250#endif
251
252int getty_main(int argc, char **argv)
253{
254 char *logname = NULL; /* login name, given to /bin/login */
255 struct chardata chardata; /* set by get_logname() */
256 struct termio termio; /* terminal mode bits */
257 static struct options options = {
258 F_ISSUE, /* show /etc/issue (SYSV_STYLE) */
259 0, /* no timeout */
260 _PATH_LOGIN, /* default login program */
261 "tty1", /* default tty line */
262 "", /* modem init string */
263 ISSUE, /* default issue file */
264 0, /* no baud rates known yet */
265 };
266
267#ifdef DEBUGGING
268 dbf = bb_xfopen(DEBUGTERM, "w");
269
270 {
271 int i;
272
273 for (i = 1; i < argc; i++) {
274 debug(argv[i]);
275 debug("\n");
276 }
277 }
278#endif
279
280 /* Parse command-line arguments. */
281
282 parse_args(argc, argv, &options);
283
284#ifdef __linux__
285 setsid();
286#endif
287
288 /* Update the utmp file. */
289
290
291#ifdef SYSV_STYLE
292#ifdef CONFIG_FEATURE_U_W_TMP
293 update_utmp(options.tty);
294#endif
295#endif
296
297 debug("calling open_tty\n");
298 /* Open the tty as standard { input, output, error }. */
299 open_tty(options.tty, &termio, options.flags & F_LOCAL);
300
301#ifdef __linux__
302 {
303 int iv;
304
305 iv = getpid();
306 ioctl(0, TIOCSPGRP, &iv);
307 }
308#endif
309 /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */
310 debug("calling termio_init\n");
311 termio_init(&termio, options.speeds[FIRST_SPEED], &options);
312
313 /* write the modem init string and DON'T flush the buffers */
314 if (options.flags & F_INITSTRING) {
315 debug("writing init string\n");
316 write(1, options.initstring, strlen(options.initstring));
317 }
318
319 if (!(options.flags & F_LOCAL)) {
320 /* go to blocking write mode unless -L is specified */
321 fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK);
322 }
323
324 /* Optionally detect the baud rate from the modem status message. */
325 debug("before autobaud\n");
326 if (options.flags & F_PARSE)
327 auto_baud(&termio);
328
329 /* Set the optional timer. */
330 if (options.timeout)
331 (void) alarm((unsigned) options.timeout);
332
333 /* optionally wait for CR or LF before writing /etc/issue */
334 if (options.flags & F_WAITCRLF) {
335 char ch;
336
337 debug("waiting for cr-lf\n");
338 while (read(0, &ch, 1) == 1) {
339 ch &= 0x7f; /* strip "parity bit" */
340#ifdef DEBUGGING
341 fprintf(dbf, "read %c\n", ch);
342#endif
343 if (ch == '\n' || ch == '\r')
344 break;
345 }
346 }
347
348 chardata = init_chardata;
349 if (!(options.flags & F_NOPROMPT)) {
350 /* Read the login name. */
351 debug("reading login name\n");
352 /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */
353 while ((logname = get_logname(&options, &chardata, &termio)) ==
354 NULL) next_speed(&termio, &options);
355 }
356
357 /* Disable timer. */
358
359 if (options.timeout)
360 (void) alarm(0);
361
362 /* Finalize the termio settings. */
363
364 termio_final(&options, &termio, &chardata);
365
366 /* Now the newline character should be properly written. */
367
368 (void) write(1, "\n", 1);
369
370 /* Let the login program take care of password validation. */
371
372 (void) execl(options.login, options.login, "--", logname, (char *) 0);
373 error("%s: can't exec %s: %m", options.tty, options.login);
374}
375
376/* parse-args - parse command-line arguments */
377
378static void parse_args(int argc, char **argv, struct options *op)
379{
380 extern char *optarg; /* getopt */
381 extern int optind; /* getopt */
382 int c;
383
384 while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) {
385 switch (c) {
386 case 'I':
387 if (!(op->initstring = strdup(optarg)))
388 error(bb_msg_memory_exhausted);
389
390 {
391 const char *p;
392 char *q;
393
394 /* copy optarg into op->initstring decoding \ddd
395 octal codes into chars */
396 q = op->initstring;
397 p = optarg;
398 while (*p) {
399 if (*p == '\\') {
400 p++;
401 *q++ = bb_process_escape_sequence(&p);
402 } else {
403 *q++ = *p++;
404 }
405 }
406 *q = '\0';
407 }
408 op->flags |= F_INITSTRING;
409 break;
410
411 case 'L': /* force local */
412 op->flags |= F_LOCAL;
413 break;
414 case 'H': /* fake login host */
415 fakehost = optarg;
416 break;
417 case 'f': /* custom issue file */
418 op->flags |= F_CUSTISSUE;
419 op->issue = optarg;
420 break;
421 case 'h': /* enable h/w flow control */
422 op->flags |= F_RTSCTS;
423 break;
424 case 'i': /* do not show /etc/issue */
425 op->flags &= ~F_ISSUE;
426 break;
427 case 'l':
428 op->login = optarg; /* non-default login program */
429 break;
430 case 'm': /* parse modem status message */
431 op->flags |= F_PARSE;
432 break;
433 case 'n':
434 op->flags |= F_NOPROMPT;
435 break;
436 case 't': /* time out */
437 if ((op->timeout = atoi(optarg)) <= 0)
438 error("bad timeout value: %s", optarg);
439 break;
440 case 'w':
441 op->flags |= F_WAITCRLF;
442 break;
443 default:
444 bb_show_usage();
445 }
446 }
447 debug("after getopt loop\n");
448 if (argc < optind + 2) /* check parameter count */
449 bb_show_usage();
450
451 /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */
452 if ('0' <= argv[optind][0] && argv[optind][0] <= '9') {
453 /* a number first, assume it's a speed (BSD style) */
454 parse_speeds(op, argv[optind++]); /* baud rate(s) */
455 op->tty = argv[optind]; /* tty name */
456 } else {
457 op->tty = argv[optind++]; /* tty name */
458 parse_speeds(op, argv[optind]); /* baud rate(s) */
459 }
460
461 optind++;
462 if (argc > optind && argv[optind])
463 setenv("TERM", argv[optind], 1);
464
465 debug("exiting parseargs\n");
466}
467
468/* parse_speeds - parse alternate baud rates */
469
470static void parse_speeds(struct options *op, char *arg)
471{
472 char *cp;
473
474 debug("entered parse_speeds\n");
475 for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) {
476 if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0)
477 error("bad speed: %s", cp);
478 if (op->numspeed > MAX_SPEED)
479 error("too many alternate speeds");
480 }
481 debug("exiting parsespeeds\n");
482}
483
484#ifdef SYSV_STYLE
485#ifdef CONFIG_FEATURE_U_W_TMP
486
487/* update_utmp - update our utmp entry */
488static void update_utmp(char *line)
489{
490 struct utmp ut;
491 struct utmp *utp;
492 time_t t;
493 int mypid = getpid();
494#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1))
495 struct flock lock;
496#endif
497
498 /*
499 * The utmp file holds miscellaneous information about things started by
500 * /sbin/init and other system-related events. Our purpose is to update
501 * the utmp entry for the current process, in particular the process type
502 * and the tty line we are listening to. Return successfully only if the
503 * utmp file can be opened for update, and if we are able to find our
504 * entry in the utmp file.
505 */
506 if (access(_PATH_UTMP, R_OK|W_OK) == -1) {
507 close(creat(_PATH_UTMP, 0664));
508 }
509 utmpname(_PATH_UTMP);
510 setutent();
511 while ((utp = getutent())
512 && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */
513 ;
514
515 if (utp) {
516 memcpy(&ut, utp, sizeof(ut));
517 } else {
518 /* some inits don't initialize utmp... */
519 memset(&ut, 0, sizeof(ut));
520 strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id));
521 }
522 /*endutent(); */
523
524 strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user));
525 strncpy(ut.ut_line, line, sizeof(ut.ut_line));
526 if (fakehost)
527 strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host));
528 time(&t);
529 ut.ut_time = t;
530 ut.ut_type = LOGIN_PROCESS;
531 ut.ut_pid = mypid;
532
533 pututline(&ut);
534 endutent();
535
536 {
537 if (access(_PATH_WTMP, R_OK|W_OK) == -1) {
538 close(creat(_PATH_WTMP, 0664));
539 }
540 updwtmp(_PATH_WTMP, &ut);
541 }
542}
543
544#endif /* CONFIG_FEATURE_U_W_TMP */
545#endif /* SYSV_STYLE */
546
547/* open_tty - set up tty as standard { input, output, error } */
548static void open_tty(char *tty, struct termio *tp, int local)
549{
550 /* Get rid of the present standard { output, error} if any. */
551
552 (void) close(1);
553 (void) close(2);
554 errno = 0; /* ignore above errors */
555
556 /* Set up new standard input, unless we are given an already opened port. */
557
558 if (strcmp(tty, "-")) {
559 struct stat st;
560
561 /* Sanity checks... */
562
563 if (chdir("/dev"))
564 error("/dev: chdir() failed: %m");
565 if (stat(tty, &st) < 0)
566 error("/dev/%s: %m", tty);
567 if ((st.st_mode & S_IFMT) != S_IFCHR)
568 error("/dev/%s: not a character device", tty);
569
570 /* Open the tty as standard input. */
571
572 (void) close(0);
573 errno = 0; /* ignore close(2) errors */
574
575 debug("open(2)\n");
576 if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0)
577 error("/dev/%s: cannot open as standard input: %m", tty);
578
579 } else {
580
581 /*
582 * Standard input should already be connected to an open port. Make
583 * sure it is open for read/write.
584 */
585
586 if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR)
587 error("%s: not open for read/write", tty);
588 }
589
590 /* Set up standard output and standard error file descriptors. */
591 debug("duping\n");
592 if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */
593 error("%s: dup problem: %m", tty); /* we have a problem */
594
595 /*
596 * The following ioctl will fail if stdin is not a tty, but also when
597 * there is noise on the modem control lines. In the latter case, the
598 * common course of action is (1) fix your cables (2) give the modem more
599 * time to properly reset after hanging up. SunOS users can achieve (2)
600 * by patching the SunOS kernel variable "zsadtrlow" to a larger value;
601 * 5 seconds seems to be a good value.
602 */
603
604 if (ioctl(0, TCGETA, tp) < 0)
605 error("%s: ioctl: %m", tty);
606
607 /*
608 * It seems to be a terminal. Set proper protections and ownership. Mode
609 * 0622 is suitable for SYSV <4 because /bin/login does not change
610 * protections. SunOS 4 login will change the protections to 0620 (write
611 * access for group tty) after the login has succeeded.
612 */
613
614#ifdef DEBIAN
615 {
616 /* tty to root.dialout 660 */
617 struct group *gr;
618 int id;
619
620 id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0;
621 chown(tty, 0, id);
622 chmod(tty, 0660);
623
624 /* vcs,vcsa to root.sys 600 */
625 if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) {
626 char *vcs, *vcsa;
627
628 if (!(vcs = strdup(tty)))
629 error("Can't malloc for vcs");
630 if (!(vcsa = malloc(strlen(tty) + 2)))
631 error("Can't malloc for vcsa");
632 strcpy(vcs, "vcs");
633 strcpy(vcs + 3, tty + 3);
634 strcpy(vcsa, "vcsa");
635 strcpy(vcsa + 4, tty + 3);
636
637 id = (gr = getgrnam("sys")) ? gr->gr_gid : 0;
638 chown(vcs, 0, id);
639 chmod(vcs, 0600);
640 chown(vcsa, 0, id);
641 chmod(vcs, 0600);
642
643 free(vcs);
644 free(vcsa);
645 }
646 }
647#else
648 (void) chown(tty, 0, 0); /* root, sys */
649 (void) chmod(tty, 0622); /* crw--w--w- */
650 errno = 0; /* ignore above errors */
651#endif
652}
653
654/* termio_init - initialize termio settings */
655
656static void termio_init(struct termio *tp, int speed, struct options *op)
657{
658
659 /*
660 * Initial termio settings: 8-bit characters, raw-mode, blocking i/o.
661 * Special characters are set after we have read the login name; all
662 * reads will be done in raw mode anyway. Errors will be dealt with
663 * lateron.
664 */
665#ifdef __linux__
666 /* flush input and output queues, important for modems! */
667 (void) ioctl(0, TCFLSH, TCIOFLUSH);
668#endif
669
670 tp->c_cflag = CS8 | HUPCL | CREAD | speed;
671 if (op->flags & F_LOCAL) {
672 tp->c_cflag |= CLOCAL;
673 }
674
675 tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0;
676 tp->c_cc[VMIN] = 1;
677 tp->c_cc[VTIME] = 0;
678
679 /* Optionally enable hardware flow control */
680
681#ifdef CRTSCTS
682 if (op->flags & F_RTSCTS)
683 tp->c_cflag |= CRTSCTS;
684#endif
685
686 (void) ioctl(0, TCSETA, tp);
687
688 /* go to blocking input even in local mode */
689 fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK);
690
691 debug("term_io 2\n");
692}
693
694/* auto_baud - extract baud rate from modem status message */
695static void auto_baud(struct termio *tp)
696{
697 int speed;
698 int vmin;
699 unsigned iflag;
700 char buf[BUFSIZ];
701 char *bp;
702 int nread;
703
704 /*
705 * This works only if the modem produces its status code AFTER raising
706 * the DCD line, and if the computer is fast enough to set the proper
707 * baud rate before the message has gone by. We expect a message of the
708 * following format:
709 *
710 * <junk><number><junk>
711 *
712 * The number is interpreted as the baud rate of the incoming call. If the
713 * modem does not tell us the baud rate within one second, we will keep
714 * using the current baud rate. It is advisable to enable BREAK
715 * processing (comma-separated list of baud rates) if the processing of
716 * modem status messages is enabled.
717 */
718
719 /*
720 * Use 7-bit characters, don't block if input queue is empty. Errors will
721 * be dealt with lateron.
722 */
723
724 iflag = tp->c_iflag;
725 tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */
726 vmin = tp->c_cc[VMIN];
727 tp->c_cc[VMIN] = 0; /* don't block if queue empty */
728 (void) ioctl(0, TCSETA, tp);
729
730 /*
731 * Wait for a while, then read everything the modem has said so far and
732 * try to extract the speed of the dial-in call.
733 */
734
735 (void) sleep(1);
736 if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) {
737 buf[nread] = '\0';
738 for (bp = buf; bp < buf + nread; bp++) {
739 if (isascii(*bp) && isdigit(*bp)) {
740 if ((speed = bcode(bp))) {
741 tp->c_cflag &= ~CBAUD;
742 tp->c_cflag |= speed;
743 }
744 break;
745 }
746 }
747 }
748 /* Restore terminal settings. Errors will be dealt with lateron. */
749
750 tp->c_iflag = iflag;
751 tp->c_cc[VMIN] = vmin;
752 (void) ioctl(0, TCSETA, tp);
753}
754
755/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */
756static void do_prompt(struct options *op, struct termio *tp)
757{
758#ifdef ISSUE /* optional: show /etc/issue */
759 print_login_issue(op->issue, op->tty);
760#endif
761 print_login_prompt();
762}
763
764/* next_speed - select next baud rate */
765static void next_speed(struct termio *tp, struct options *op)
766{
767 static int baud_index = FIRST_SPEED; /* current speed index */
768
769 baud_index = (baud_index + 1) % op->numspeed;
770 tp->c_cflag &= ~CBAUD;
771 tp->c_cflag |= op->speeds[baud_index];
772 (void) ioctl(0, TCSETA, tp);
773}
774
775/* get_logname - get user name, establish parity, speed, erase, kill, eol */
776/* return NULL on failure, logname on success */
777static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp)
778{
779 static char logname[BUFSIZ];
780 char *bp;
781 char c; /* input character, full eight bits */
782 char ascval; /* low 7 bits of input character */
783 int bits; /* # of "1" bits per character */
784 int mask; /* mask with 1 bit up */
785 static char *erase[] = { /* backspace-space-backspace */
786 "\010\040\010", /* space parity */
787 "\010\040\010", /* odd parity */
788 "\210\240\210", /* even parity */
789 "\210\240\210", /* no parity */
790 };
791
792 /* Initialize kill, erase, parity etc. (also after switching speeds). */
793
794 *cp = init_chardata;
795
796 /* Flush pending input (esp. after parsing or switching the baud rate). */
797
798 (void) sleep(1);
799 (void) ioctl(0, TCFLSH, TCIFLUSH);
800
801 /* Prompt for and read a login name. */
802
803 for (*logname = 0; *logname == 0; /* void */ ) {
804
805 /* Write issue file and prompt, with "parity" bit == 0. */
806
807 do_prompt(op, tp);
808
809 /* Read name, watch for break, parity, erase, kill, end-of-line. */
810
811 for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) {
812
813 /* Do not report trivial EINTR/EIO errors. */
814
815 if (read(0, &c, 1) < 1) {
816 if (errno == EINTR || errno == EIO)
817 exit(0);
818 error("%s: read: %m", op->tty);
819 }
820 /* Do BREAK handling elsewhere. */
821
822 if ((c == 0) && op->numspeed > 1)
823 /* return (0); */
824 return NULL;
825
826 /* Do parity bit handling. */
827
828 if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */
829 for (bits = 1, mask = 1; mask & 0177; mask <<= 1)
830 if (mask & ascval)
831 bits++; /* count "1" bits */
832 cp->parity |= ((bits & 1) ? 1 : 2);
833 }
834 /* Do erase, kill and end-of-line processing. */
835
836 switch (ascval) {
837 case CR:
838 case NL:
839 *bp = 0; /* terminate logname */
840 cp->eol = ascval; /* set end-of-line char */
841 break;
842 case BS:
843 case DEL:
844 case '#':
845 cp->erase = ascval; /* set erase character */
846 if (bp > logname) {
847 (void) write(1, erase[cp->parity], 3);
848 bp--;
849 }
850 break;
851 case CTL('U'):
852 case '@':
853 cp->kill = ascval; /* set kill character */
854 while (bp > logname) {
855 (void) write(1, erase[cp->parity], 3);
856 bp--;
857 }
858 break;
859 case CTL('D'):
860 exit(0);
861 default:
862 if (!isascii(ascval) || !isprint(ascval)) {
863 /* ignore garbage characters */ ;
864 } else if (bp - logname >= sizeof(logname) - 1) {
865 error("%s: input overrun", op->tty);
866 } else {
867 (void) write(1, &c, 1); /* echo the character */
868 *bp++ = ascval; /* and store it */
869 }
870 break;
871 }
872 }
873 }
874 /* Handle names with upper case and no lower case. */
875
876 if ((cp->capslock = caps_lock(logname))) {
877 for (bp = logname; *bp; bp++)
878 if (isupper(*bp))
879 *bp = tolower(*bp); /* map name to lower case */
880 }
881 return (logname);
882}
883
884/* termio_final - set the final tty mode bits */
885static void termio_final(struct options *op, struct termio *tp, struct chardata *cp)
886{
887 /* General terminal-independent stuff. */
888
889 tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */
890 tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE;
891 /* no longer| ECHOCTL | ECHOPRT */
892 tp->c_oflag |= OPOST;
893 /* tp->c_cflag = 0; */
894 tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */
895 tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */
896 tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */
897 tp->c_cc[VEOL] = DEF_EOL;
898 tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */
899
900 /* Account for special characters seen in input. */
901
902 if (cp->eol == CR) {
903 tp->c_iflag |= ICRNL; /* map CR in input to NL */
904 tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */
905 }
906 tp->c_cc[VERASE] = cp->erase; /* set erase character */
907 tp->c_cc[VKILL] = cp->kill; /* set kill character */
908
909 /* Account for the presence or absence of parity bits in input. */
910
911 switch (cp->parity) {
912 case 0: /* space (always 0) parity */
913 break;
914 case 1: /* odd parity */
915 tp->c_cflag |= PARODD;
916 /* FALLTHROUGH */
917 case 2: /* even parity */
918 tp->c_cflag |= PARENB;
919 tp->c_iflag |= INPCK | ISTRIP;
920 /* FALLTHROUGH */
921 case (1 | 2): /* no parity bit */
922 tp->c_cflag &= ~CSIZE;
923 tp->c_cflag |= CS7;
924 break;
925 }
926 /* Account for upper case without lower case. */
927
928 if (cp->capslock) {
929 tp->c_iflag |= IUCLC;
930 tp->c_lflag |= XCASE;
931 tp->c_oflag |= OLCUC;
932 }
933 /* Optionally enable hardware flow control */
934
935#ifdef CRTSCTS
936 if (op->flags & F_RTSCTS)
937 tp->c_cflag |= CRTSCTS;
938#endif
939
940 /* Finally, make the new settings effective */
941
942 if (ioctl(0, TCSETA, tp) < 0)
943 error("%s: ioctl: TCSETA: %m", op->tty);
944}
945
946/* caps_lock - string contains upper case without lower case */
947/* returns 1 if true, 0 if false */
948static int caps_lock(const char *s)
949{
950 int capslock;
951
952 for (capslock = 0; *s; s++) {
953 if (islower(*s))
954 return (0);
955 if (capslock == 0)
956 capslock = isupper(*s);
957 }
958 return (capslock);
959}
960
961/* bcode - convert speed string to speed code; return 0 on failure */
962static int bcode(char *s)
963{
964 int r;
965 unsigned long value;
966 if (safe_strtoul(s, &value)) {
967 return -1;
968 }
969 if ((r = bb_value_to_baud(value)) > 0) {
970 return r;
971 }
972 return 0;
973}
974
975/* error - report errors to console or syslog; only understands %s and %m */
976
977#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2)
978
979/*
980 * output error messages
981 */
982static void error(const char *fmt, ...)
983{
984 va_list va_alist;
985 char buf[256], *bp;
986
987#ifndef USE_SYSLOG
988 int fd;
989#endif
990
991#ifdef USE_SYSLOG
992 buf[0] = '\0';
993 bp = buf;
994#else
995 strncpy(buf, bb_applet_name, 256);
996 strncat(buf, ": ", 256);
997 buf[255] = 0;
998 bp = buf + strlen(buf);
999#endif
1000
1001 va_start(va_alist, fmt);
1002 vsnprintf(bp, 256 - strlen(buf), fmt, va_alist);
1003 buf[255] = 0;
1004 va_end(va_alist);
1005
1006#ifdef USE_SYSLOG
1007 openlog(bb_applet_name, 0, LOG_AUTH);
1008 syslog(LOG_ERR, "%s", buf);
1009 closelog();
1010#else
1011 strncat(bp, "\r\n", 256 - strlen(buf));
1012 buf[255] = 0;
1013 if ((fd = open("/dev/console", 1)) >= 0) {
1014 write(fd, buf, strlen(buf));
1015 close(fd);
1016 }
1017#endif
1018 (void) sleep((unsigned) 10); /* be kind to init(8) */
1019 exit(1);
1020}
diff --git a/busybox/loginutils/login.c b/busybox/loginutils/login.c
new file mode 100644
index 000000000..f3630f102
--- /dev/null
+++ b/busybox/loginutils/login.c
@@ -0,0 +1,486 @@
1/* vi: set sw=4 ts=4: */
2#include <fcntl.h>
3#include <signal.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <syslog.h>
8#include <termios.h>
9#include <unistd.h>
10#include <utmp.h>
11#include <sys/resource.h>
12#include <sys/stat.h>
13#include <sys/time.h>
14#include <sys/types.h>
15#include <ctype.h>
16#include <time.h>
17
18#include "busybox.h"
19#ifdef CONFIG_SELINUX
20#include <flask_util.h>
21#include <get_sid_list.h>
22#include <proc_secure.h>
23#include <fs_secure.h>
24#endif
25
26#ifdef CONFIG_FEATURE_U_W_TMP
27// import from utmp.c
28static void checkutmp(int picky);
29static void setutmp(const char *name, const char *line);
30/* Stuff global to this file */
31struct utmp utent;
32#endif
33
34// login defines
35#define TIMEOUT 60
36#define EMPTY_USERNAME_COUNT 10
37#define USERNAME_SIZE 32
38
39
40static int check_nologin ( int amroot );
41
42#if defined CONFIG_FEATURE_SECURETTY
43static int check_tty ( const char *tty );
44
45#else
46static inline int check_tty ( const char *tty ) { return 1; }
47
48#endif
49
50static int is_my_tty ( const char *tty );
51static int login_prompt ( char *buf_name );
52static void motd ( void );
53
54
55static void alarm_handler ( int sig )
56{
57 fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT );
58 exit ( EXIT_SUCCESS );
59}
60
61
62extern int login_main(int argc, char **argv)
63{
64 char tty[BUFSIZ];
65 char full_tty[200];
66 char fromhost[512];
67 char username[USERNAME_SIZE];
68 const char *tmp;
69 int amroot;
70 int flag;
71 int failed;
72 int count=0;
73 struct passwd *pw, pw_copy;
74#ifdef CONFIG_WHEEL_GROUP
75 struct group *grp;
76#endif
77 int opt_preserve = 0;
78 int opt_fflag = 0;
79 char *opt_host = 0;
80 int alarmstarted = 0;
81#ifdef CONFIG_SELINUX
82 int flask_enabled = is_flask_enabled();
83 security_id_t sid = 0, old_tty_sid, new_tty_sid;
84#endif
85
86 username[0]=0;
87 amroot = ( getuid ( ) == 0 );
88 signal ( SIGALRM, alarm_handler );
89 alarm ( TIMEOUT );
90 alarmstarted = 1;
91
92 while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) {
93 switch ( flag ) {
94 case 'p':
95 opt_preserve = 1;
96 break;
97 case 'f':
98 /*
99 * username must be a separate token
100 * (-f root, *NOT* -froot). --marekm
101 */
102 if ( optarg != argv[optind-1] )
103 bb_show_usage( );
104
105 if ( !amroot ) /* Auth bypass only if real UID is zero */
106 bb_error_msg_and_die ( "-f permission denied" );
107
108 safe_strncpy(username, optarg, USERNAME_SIZE);
109 opt_fflag = 1;
110 break;
111 case 'h':
112 opt_host = optarg;
113 break;
114 default:
115 bb_show_usage( );
116 }
117 }
118
119 if (optind < argc) // user from command line (getty)
120 safe_strncpy(username, argv[optind], USERNAME_SIZE);
121
122 if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 ))
123 return EXIT_FAILURE; /* Must be a terminal */
124
125#ifdef CONFIG_FEATURE_U_W_TMP
126 checkutmp ( !amroot );
127#endif
128
129 tmp = ttyname ( 0 );
130 if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 ))
131 safe_strncpy ( tty, tmp + 5, sizeof( tty ));
132 else if ( tmp && *tmp == '/' )
133 safe_strncpy ( tty, tmp, sizeof( tty ));
134 else
135 safe_strncpy ( tty, "UNKNOWN", sizeof( tty ));
136
137#ifdef CONFIG_FEATURE_U_W_TMP
138 if ( amroot )
139 memset ( utent.ut_host, 0, sizeof utent.ut_host );
140#endif
141
142 if ( opt_host ) {
143#ifdef CONFIG_FEATURE_U_W_TMP
144 safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host ));
145#endif
146 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host );
147 }
148 else
149 snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty );
150
151 setpgrp();
152
153 openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH );
154
155 while ( 1 ) {
156 failed = 0;
157
158 if ( !username[0] )
159 if(!login_prompt ( username ))
160 return EXIT_FAILURE;
161
162 if ( !alarmstarted && ( TIMEOUT > 0 )) {
163 alarm ( TIMEOUT );
164 alarmstarted = 1;
165 }
166
167 if (!( pw = getpwnam ( username ))) {
168 pw_copy.pw_name = "UNKNOWN";
169 pw_copy.pw_passwd = "!";
170 opt_fflag = 0;
171 failed = 1;
172 } else
173 pw_copy = *pw;
174
175 pw = &pw_copy;
176
177 if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' ))
178 failed = 1;
179
180 if ( opt_fflag ) {
181 opt_fflag = 0;
182 goto auth_ok;
183 }
184
185 if (!failed && ( pw-> pw_uid == 0 ) && ( !check_tty ( tty )))
186 failed = 1;
187
188 /* Don't check the password if password entry is empty (!) */
189 if ( !pw-> pw_passwd[0] )
190 goto auth_ok;
191
192 /* authorization takes place here */
193 if ( correct_password ( pw ))
194 goto auth_ok;
195
196 failed = 1;
197
198auth_ok:
199 if ( !failed)
200 break;
201
202 { // delay next try
203 time_t start, now;
204
205 time ( &start );
206 now = start;
207 while ( difftime ( now, start ) < FAIL_DELAY) {
208 sleep ( FAIL_DELAY );
209 time ( &now );
210 }
211 }
212
213 puts("Login incorrect");
214 username[0] = 0;
215 if ( ++count == 3 ) {
216 syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw->pw_name, fromhost);
217 return EXIT_FAILURE;
218 }
219 }
220
221 alarm ( 0 );
222 if ( check_nologin ( pw-> pw_uid == 0 ))
223 return EXIT_FAILURE;
224
225#ifdef CONFIG_FEATURE_U_W_TMP
226 setutmp ( username, tty );
227#endif
228#ifdef CONFIG_SELINUX
229 if (flask_enabled)
230 {
231 struct stat st;
232
233 if (get_default_sid(username, 0, &sid))
234 {
235 fprintf(stderr, "Unable to get SID for %s\n", username);
236 exit(1);
237 }
238 if (stat_secure(tty, &st, &old_tty_sid))
239 {
240 fprintf(stderr, "stat_secure(%.100s) failed: %.100s\n", tty, strerror(errno));
241 return EXIT_FAILURE;
242 }
243 if (security_change_sid (sid, old_tty_sid, SECCLASS_CHR_FILE, &new_tty_sid) != 0)
244 {
245 fprintf(stderr, "security_change_sid(%.100s) failed: %.100s\n", tty, strerror(errno));
246 return EXIT_FAILURE;
247 }
248 if(chsid(tty, new_tty_sid) != 0)
249 {
250 fprintf(stderr, "chsid(%.100s, %d) failed: %.100s\n", tty, new_tty_sid, strerror(errno));
251 return EXIT_FAILURE;
252 }
253 }
254 else
255 sid = 0;
256#endif
257
258 if ( *tty != '/' )
259 snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty);
260 else
261 safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 );
262
263 if ( !is_my_tty ( full_tty ))
264 syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty );
265
266 /* Try these, but don't complain if they fail
267 * (for example when the root fs is read only) */
268 chown ( full_tty, pw-> pw_uid, pw-> pw_gid );
269 chmod ( full_tty, 0600 );
270
271 change_identity ( pw );
272 tmp = pw-> pw_shell;
273 if(!tmp || !*tmp)
274 tmp = DEFAULT_SHELL;
275 setup_environment ( tmp, 1, !opt_preserve, pw );
276
277 motd ( );
278 signal ( SIGALRM, SIG_DFL ); /* default alarm signal */
279
280 if ( pw-> pw_uid == 0 )
281 syslog ( LOG_INFO, "root login %s\n", fromhost );
282 run_shell ( tmp, 1, 0, 0
283#ifdef CONFIG_SELINUX
284 , sid
285#endif
286 ); /* exec the shell finally. */
287
288 return EXIT_FAILURE;
289}
290
291
292
293static int login_prompt ( char *buf_name )
294{
295 char buf [1024];
296 char *sp, *ep;
297 int i;
298
299 for(i=0; i<EMPTY_USERNAME_COUNT; i++) {
300 print_login_prompt();
301
302 if ( !fgets ( buf, sizeof( buf ) - 1, stdin ))
303 return 0;
304
305 if ( !strchr ( buf, '\n' ))
306 return 0;
307
308 for ( sp = buf; isspace ( *sp ); sp++ ) { }
309 for ( ep = sp; isgraph ( *ep ); ep++ ) { }
310
311 *ep = 0;
312 safe_strncpy(buf_name, sp, USERNAME_SIZE);
313 if(buf_name[0])
314 return 1;
315 }
316 return 0;
317}
318
319
320static int check_nologin ( int amroot )
321{
322 if ( access ( bb_path_nologin_file, F_OK ) == 0 ) {
323 FILE *fp;
324 int c;
325
326 if (( fp = fopen ( bb_path_nologin_file, "r" ))) {
327 while (( c = getc ( fp )) != EOF )
328 putchar (( c == '\n' ) ? '\r' : c );
329
330 fflush ( stdout );
331 fclose ( fp );
332 } else {
333 puts ( "\r\nSystem closed for routine maintenance.\r" );
334 }
335 if ( !amroot )
336 return 1;
337
338 puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" );
339 }
340 return 0;
341}
342
343#ifdef CONFIG_FEATURE_SECURETTY
344
345static int check_tty ( const char *tty )
346{
347 FILE *fp;
348 int i;
349 char buf[BUFSIZ];
350
351 if (( fp = fopen ( bb_path_securetty_file, "r" ))) {
352 while ( fgets ( buf, sizeof( buf ) - 1, fp )) {
353 for ( i = bb_strlen( buf ) - 1; i >= 0; --i ) {
354 if ( !isspace ( buf[i] ))
355 break;
356 }
357 buf[++i] = '\0';
358 if (( buf [0] == '\0' ) || ( buf [0] == '#' ))
359 continue;
360
361 if ( strcmp ( buf, tty ) == 0 ) {
362 fclose ( fp );
363 return 1;
364 }
365 }
366 fclose(fp);
367 return 0;
368 }
369 /* A missing securetty file is not an error. */
370 return 1;
371}
372
373#endif
374
375/* returns 1 if true */
376static int is_my_tty ( const char *tty )
377{
378 struct stat by_name, by_fd;
379
380 if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd ))
381 return 0;
382
383 if ( by_name. st_rdev != by_fd. st_rdev )
384 return 0;
385 else
386 return 1;
387}
388
389
390static void motd ( )
391{
392 FILE *fp;
393 register int c;
394
395 if (( fp = fopen ( bb_path_motd_file, "r" ))) {
396 while (( c = getc ( fp )) != EOF )
397 putchar ( c );
398 fclose ( fp );
399 }
400}
401
402
403#ifdef CONFIG_FEATURE_U_W_TMP
404// vv Taken from tinylogin utmp.c vv
405
406#define NO_UTENT \
407 "No utmp entry. You must exec \"login\" from the lowest level \"sh\""
408#define NO_TTY \
409 "Unable to determine your tty name."
410
411/*
412 * checkutmp - see if utmp file is correct for this process
413 *
414 * System V is very picky about the contents of the utmp file
415 * and requires that a slot for the current process exist.
416 * The utmp file is scanned for an entry with the same process
417 * ID. If no entry exists the process exits with a message.
418 *
419 * The "picky" flag is for network and other logins that may
420 * use special flags. It allows the pid checks to be overridden.
421 * This means that getty should never invoke login with any
422 * command line flags.
423 */
424
425static void checkutmp(int picky)
426{
427 char *line;
428 struct utmp *ut;
429 pid_t pid = getpid();
430
431 setutent();
432
433 /* First, try to find a valid utmp entry for this process. */
434 while ((ut = getutent()))
435 if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] &&
436 (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS))
437 break;
438
439 /* If there is one, just use it, otherwise create a new one. */
440 if (ut) {
441 utent = *ut;
442 } else {
443 if (picky) {
444 puts(NO_UTENT);
445 exit(1);
446 }
447 line = ttyname(0);
448 if (!line) {
449 puts(NO_TTY);
450 exit(1);
451 }
452 if (strncmp(line, "/dev/", 5) == 0)
453 line += 5;
454 memset((void *) &utent, 0, sizeof utent);
455 utent.ut_type = LOGIN_PROCESS;
456 utent.ut_pid = pid;
457 strncpy(utent.ut_line, line, sizeof utent.ut_line);
458 /* XXX - assumes /dev/tty?? */
459 strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id);
460 strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user);
461 time(&utent.ut_time);
462 }
463}
464
465/*
466 * setutmp - put a USER_PROCESS entry in the utmp file
467 *
468 * setutmp changes the type of the current utmp entry to
469 * USER_PROCESS. the wtmp file will be updated as well.
470 */
471
472static void setutmp(const char *name, const char *line)
473{
474 utent.ut_type = USER_PROCESS;
475 strncpy(utent.ut_user, name, sizeof utent.ut_user);
476 time(&utent.ut_time);
477 /* other fields already filled in by checkutmp above */
478 setutent();
479 pututline(&utent);
480 endutent();
481 if (access(_PATH_WTMP, R_OK|W_OK) == -1) {
482 close(creat(_PATH_WTMP, 0664));
483 }
484 updwtmp(_PATH_WTMP, &utent);
485}
486#endif /* CONFIG_FEATURE_U_W_TMP */
diff --git a/busybox/loginutils/passwd.c b/busybox/loginutils/passwd.c
new file mode 100644
index 000000000..9c4b4ddfb
--- /dev/null
+++ b/busybox/loginutils/passwd.c
@@ -0,0 +1,400 @@
1/* vi: set sw=4 ts=4: */
2#include <fcntl.h>
3#include <stdio.h>
4#include <string.h>
5#include <signal.h>
6#include <sys/stat.h>
7#include <sys/types.h>
8#include <unistd.h>
9#include <utime.h>
10#include <syslog.h>
11#include <time.h>
12#include <sys/resource.h>
13#include <errno.h>
14
15#include "busybox.h"
16
17static char crypt_passwd[128];
18
19static int create_backup(const char *backup, FILE * fp);
20static int new_password(const struct passwd *pw, int amroot, int algo);
21static void set_filesize_limit(int blocks);
22
23
24int get_algo(char *a)
25{
26 int x = 1; /* standard: MD5 */
27
28 if (strcasecmp(a, "des") == 0)
29 x = 0;
30 return x;
31}
32
33
34extern int update_passwd(const struct passwd *pw, char *crypt_pw)
35{
36 char filename[1024];
37 char buf[1025];
38 char buffer[80];
39 char username[32];
40 char *pw_rest;
41 int mask;
42 int continued;
43 FILE *fp;
44 FILE *out_fp;
45 struct stat sb;
46 struct flock lock;
47
48#ifdef CONFIG_FEATURE_SHADOWPASSWDS
49 if (access(bb_path_shadow_file, F_OK) == 0) {
50 snprintf(filename, sizeof filename, "%s", bb_path_shadow_file);
51 } else
52#endif
53 {
54 snprintf(filename, sizeof filename, "%s", bb_path_passwd_file);
55 }
56
57 if (((fp = fopen(filename, "r+")) == 0) || (fstat(fileno(fp), &sb))) {
58 /* return 0; */
59 return 1;
60 }
61
62 /* Lock the password file before updating */
63 lock.l_type = F_WRLCK;
64 lock.l_whence = SEEK_SET;
65 lock.l_start = 0;
66 lock.l_len = 0;
67 if (fcntl(fileno(fp), F_SETLK, &lock) < 0) {
68 fprintf(stderr, "%s: %s\n", filename, strerror(errno));
69 return 1;
70 }
71 lock.l_type = F_UNLCK;
72
73 snprintf(buf, sizeof buf, "%s-", filename);
74 if (create_backup(buf, fp)) {
75 fcntl(fileno(fp), F_SETLK, &lock);
76 fclose(fp);
77 return 1;
78 }
79 snprintf(buf, sizeof buf, "%s+", filename);
80 mask = umask(0777);
81 out_fp = fopen(buf, "w");
82 umask(mask);
83 if ((!out_fp) || (fchmod(fileno(out_fp), sb.st_mode & 0777))
84 || (fchown(fileno(out_fp), sb.st_uid, sb.st_gid))) {
85 fcntl(fileno(fp), F_SETLK, &lock);
86 fclose(fp);
87 fclose(out_fp);
88 return 1;
89 }
90
91 continued = 0;
92 snprintf(username, sizeof username, "%s:", pw->pw_name);
93 rewind(fp);
94 while (!feof(fp)) {
95 fgets(buffer, sizeof buffer, fp);
96 if (!continued) { // Check to see if we're updating this line.
97 if (strncmp(username, buffer, strlen(username)) == 0) { // we have a match.
98 pw_rest = strchr(buffer, ':');
99 *pw_rest++ = '\0';
100 pw_rest = strchr(pw_rest, ':');
101 fprintf(out_fp, "%s:%s%s", buffer, crypt_pw, pw_rest);
102 } else {
103 fputs(buffer, out_fp);
104 }
105 } else {
106 fputs(buffer, out_fp);
107 }
108 if (buffer[strlen(buffer) - 1] == '\n') {
109 continued = 0;
110 } else {
111 continued = 1;
112 }
113 bzero(buffer, sizeof buffer);
114 }
115
116 if (fflush(out_fp) || fsync(fileno(out_fp)) || fclose(out_fp)) {
117 unlink(buf);
118 fcntl(fileno(fp), F_SETLK, &lock);
119 fclose(fp);
120 return 1;
121 }
122 if (rename(buf, filename) < 0) {
123 fcntl(fileno(fp), F_SETLK, &lock);
124 fclose(fp);
125 return 1;
126 } else {
127 fcntl(fileno(fp), F_SETLK, &lock);
128 fclose(fp);
129 return 0;
130 }
131}
132
133
134extern int passwd_main(int argc, char **argv)
135{
136 int amroot;
137 char *cp;
138 char *np;
139 char *name;
140 char *myname;
141 int flag;
142 int algo = 1; /* -a - password algorithm */
143 int lflg = 0; /* -l - lock account */
144 int uflg = 0; /* -u - unlock account */
145 int dflg = 0; /* -d - delete password */
146 const struct passwd *pw;
147
148#ifdef CONFIG_FEATURE_SHADOWPASSWDS
149 const struct spwd *sp;
150#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
151 amroot = (getuid() == 0);
152 openlog("passwd", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
153 while ((flag = getopt(argc, argv, "a:dlu")) != EOF) {
154 switch (flag) {
155 case 'a':
156 algo = get_algo(optarg);
157 break;
158 case 'd':
159 dflg++;
160 break;
161 case 'l':
162 lflg++;
163 break;
164 case 'u':
165 uflg++;
166 break;
167 default:
168 bb_show_usage();
169 }
170 }
171 myname = (char *) bb_xstrdup(my_getpwuid(NULL, getuid(), -1));
172 /* exits on error */
173 if (optind < argc) {
174 name = argv[optind];
175 } else {
176 name = myname;
177 }
178 if ((lflg || uflg || dflg) && (optind >= argc || !amroot)) {
179 bb_show_usage();
180 }
181 pw = getpwnam(name);
182 if (!pw) {
183 bb_error_msg_and_die("Unknown user %s\n", name);
184 }
185 if (!amroot && pw->pw_uid != getuid()) {
186 syslog(LOG_WARNING, "can't change pwd for `%s'", name);
187 bb_error_msg_and_die("Permission denied.\n");
188 }
189#ifdef CONFIG_FEATURE_SHADOWPASSWDS
190 sp = getspnam(name);
191 if (!sp) {
192 sp = (struct spwd *) pwd_to_spwd(pw);
193 }
194 cp = sp->sp_pwdp;
195 np = sp->sp_namp;
196#else
197 cp = pw->pw_passwd;
198 np = name;
199#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
200
201 safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
202 if (!(dflg || lflg || uflg)) {
203 if (!amroot) {
204 if (cp[0] == '!') {
205 syslog(LOG_WARNING, "password locked for `%s'", np);
206 bb_error_msg_and_die( "The password for `%s' cannot be changed.\n", np);
207 }
208 }
209 printf("Changing password for %s\n", name);
210 if (new_password(pw, amroot, algo)) {
211 bb_error_msg_and_die( "The password for %s is unchanged.\n", name);
212 }
213 } else if (lflg) {
214 if (crypt_passwd[0] != '!') {
215 memmove(&crypt_passwd[1], crypt_passwd,
216 sizeof crypt_passwd - 1);
217 crypt_passwd[sizeof crypt_passwd - 1] = '\0';
218 crypt_passwd[0] = '!';
219 }
220 } else if (uflg) {
221 if (crypt_passwd[0] == '!') {
222 memmove(crypt_passwd, &crypt_passwd[1],
223 sizeof crypt_passwd - 1);
224 }
225 } else if (dflg) {
226 crypt_passwd[0] = '\0';
227 }
228 set_filesize_limit(30000);
229 signal(SIGHUP, SIG_IGN);
230 signal(SIGINT, SIG_IGN);
231 signal(SIGQUIT, SIG_IGN);
232 umask(077);
233 if (setuid(0)) {
234 syslog(LOG_ERR, "can't setuid(0)");
235 bb_error_msg_and_die( "Cannot change ID to root.\n");
236 }
237 if (!update_passwd(pw, crypt_passwd)) {
238 syslog(LOG_INFO, "password for `%s' changed by user `%s'", name,
239 myname);
240 printf("Password changed.\n");
241 } else {
242 syslog(LOG_WARNING, "an error occurred updating the password file");
243 bb_error_msg_and_die("An error occurred updating the password file.\n");
244 }
245 return (0);
246}
247
248
249
250static int create_backup(const char *backup, FILE * fp)
251{
252 struct stat sb;
253 struct utimbuf ub;
254 FILE *bkfp;
255 int c, mask;
256
257 if (fstat(fileno(fp), &sb))
258 /* return -1; */
259 return 1;
260
261 mask = umask(077);
262 bkfp = fopen(backup, "w");
263 umask(mask);
264 if (!bkfp)
265 /* return -1; */
266 return 1;
267
268 /* TODO: faster copy, not one-char-at-a-time. --marekm */
269 rewind(fp);
270 while ((c = getc(fp)) != EOF) {
271 if (putc(c, bkfp) == EOF)
272 break;
273 }
274 if (c != EOF || fflush(bkfp)) {
275 fclose(bkfp);
276 /* return -1; */
277 return 1;
278 }
279 if (fclose(bkfp))
280 /* return -1; */
281 return 1;
282
283 ub.actime = sb.st_atime;
284 ub.modtime = sb.st_mtime;
285 utime(backup, &ub);
286 return 0;
287}
288
289static int i64c(int i)
290{
291 if (i <= 0)
292 return ('.');
293 if (i == 1)
294 return ('/');
295 if (i >= 2 && i < 12)
296 return ('0' - 2 + i);
297 if (i >= 12 && i < 38)
298 return ('A' - 12 + i);
299 if (i >= 38 && i < 63)
300 return ('a' - 38 + i);
301 return ('z');
302}
303
304static char *crypt_make_salt(void)
305{
306 time_t now;
307 static unsigned long x;
308 static char result[3];
309
310 time(&now);
311 x += now + getpid() + clock();
312 result[0] = i64c(((x >> 18) ^ (x >> 6)) & 077);
313 result[1] = i64c(((x >> 12) ^ x) & 077);
314 result[2] = '\0';
315 return result;
316}
317
318
319static int new_password(const struct passwd *pw, int amroot, int algo)
320{
321 char *clear;
322 char *cipher;
323 char *cp;
324 char orig[200];
325 char pass[200];
326 time_t start, now;
327
328 if (!amroot && crypt_passwd[0]) {
329 if (!(clear = bb_askpass(0, "Old password:"))) {
330 /* return -1; */
331 return 1;
332 }
333 cipher = pw_encrypt(clear, crypt_passwd);
334 if (strcmp(cipher, crypt_passwd) != 0) {
335 syslog(LOG_WARNING, "incorrect password for `%s'",
336 pw->pw_name);
337 time(&start);
338 now = start;
339 while (difftime(now, start) < FAIL_DELAY) {
340 sleep(FAIL_DELAY);
341 time(&now);
342 }
343 fprintf(stderr, "Incorrect password.\n");
344 /* return -1; */
345 return 1;
346 }
347 safe_strncpy(orig, clear, sizeof(orig));
348 bzero(clear, strlen(clear));
349 bzero(cipher, strlen(cipher));
350 } else {
351 orig[0] = '\0';
352 }
353 if (! (cp=bb_askpass(0, "Enter the new password (minimum of 5, maximum of 8 characters)\n"
354 "Please use a combination of upper and lower case letters and numbers.\n"
355 "Enter new password: ")))
356 {
357 bzero(orig, sizeof orig);
358 /* return -1; */
359 return 1;
360 }
361 safe_strncpy(pass, cp, sizeof(pass));
362 bzero(cp, strlen(cp));
363 /* if (!obscure(orig, pass, pw)) { */
364 if (obscure(orig, pass, pw)) {
365 if (amroot) {
366 printf("\nWarning: weak password (continuing).\n");
367 } else {
368 /* return -1; */
369 return 1;
370 }
371 }
372 if (!(cp = bb_askpass(0, "Re-enter new password: "))) {
373 bzero(orig, sizeof orig);
374 /* return -1; */
375 return 1;
376 }
377 if (strcmp(cp, pass)) {
378 fprintf(stderr, "Passwords do not match.\n");
379 /* return -1; */
380 return 1;
381 }
382 bzero(cp, strlen(cp));
383 bzero(orig, sizeof(orig));
384
385 if (algo == 1) {
386 cp = pw_encrypt(pass, "$1$");
387 } else
388 cp = pw_encrypt(pass, crypt_make_salt());
389 bzero(pass, sizeof pass);
390 safe_strncpy(crypt_passwd, cp, sizeof(crypt_passwd));
391 return 0;
392}
393
394static void set_filesize_limit(int blocks)
395{
396 struct rlimit rlimit_fsize;
397
398 rlimit_fsize.rlim_cur = rlimit_fsize.rlim_max = 512L * blocks;
399 setrlimit(RLIMIT_FSIZE, &rlimit_fsize);
400}
diff --git a/busybox/loginutils/su.c b/busybox/loginutils/su.c
new file mode 100644
index 000000000..ec0c16c7d
--- /dev/null
+++ b/busybox/loginutils/su.c
@@ -0,0 +1,157 @@
1/* vi: set sw=4 ts=4: */
2
3#include <fcntl.h>
4#include <signal.h>
5#include <stdio.h>
6#include <stdlib.h>
7#include <string.h>
8#include <syslog.h>
9#include <termios.h>
10#include <unistd.h>
11#include <utmp.h>
12#include <sys/resource.h>
13#include <sys/stat.h>
14#include <sys/time.h>
15#include <sys/types.h>
16#include <ctype.h>
17#include <time.h>
18
19#include "busybox.h"
20
21
22
23/* The shell to run if none is given in the user's passwd entry. */
24#define DEFAULT_USER "root"
25
26//#define SYSLOG_SUCCESS
27#define SYSLOG_FAILURE
28
29
30#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
31/* Log the fact that someone has run su */
32
33# if defined( SYSLOG_SUCCESS ) && defined( SYSLOG_FAILURE )
34static void log_su (const char *successful, const char *old_user, const char *tty)
35{
36 syslog ( LOG_NOTICE, "%s%s on %s", successful, old_user, tty);
37}
38# define log_su_successful(cu, u, tty) if(!cu) log_su("", u, tty)
39# define log_su_failure(cu, u, tty) if(!cu) log_su("FAILED SU ", u, tty)
40# else
41 /* partial logging */
42# if !defined( SYSLOG_SUCESS )
43# define log_su_successful(cu, u, tty)
44# define log_su_failure(cu, u, t) if(!cu) \
45 syslog(LOG_NOTICE, "FAILED SU %s on %s", u, t)
46# else
47# define log_su_successful(cu, u, t) if(!cu) \
48 syslog(LOG_NOTICE, "%s on %s", u, t)
49# define log_su_failure(cu, u, tty)
50# endif
51# endif
52#else
53 /* logging not used */
54# define log_su_successful(cu, u, tty)
55# define log_su_failure(cu, u, tty)
56#endif
57
58
59int su_main ( int argc, char **argv )
60{
61 unsigned long flags;
62 int opt_preserve;
63 int opt_loginshell;
64 char *opt_shell = 0;
65 char *opt_command = 0;
66 char *opt_username = DEFAULT_USER;
67 char **opt_args = 0;
68 struct passwd *pw;
69 uid_t cur_uid = getuid();
70
71#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
72 const char *tty;
73 const char *old_user;
74#endif
75
76 flags = bb_getopt_ulflags(argc, argv, "mplc:s:",
77 &opt_command, &opt_shell);
78 opt_preserve = flags & 3;
79 opt_loginshell = (flags & 4 ? 1 : 0);
80
81 if (optind < argc && argv[optind][0] == '-' && argv[optind][1] == 0) {
82 opt_loginshell = 1;
83 ++optind;
84 }
85
86 /* get user if specified */
87 if ( optind < argc )
88 opt_username = argv [optind++];
89
90 if ( optind < argc )
91 opt_args = argv + optind;
92
93#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
94#ifdef CONFIG_FEATURE_U_W_TMP
95 /* The utmp entry (via getlogin) is probably the best way to identify
96 the user, especially if someone su's from a su-shell. */
97 old_user = getlogin ( );
98 if ( !old_user )
99#endif
100 {
101 /* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid. */
102 pw = getpwuid ( cur_uid );
103 old_user = ( pw ? pw->pw_name : "" );
104 }
105 tty = ttyname ( 2 );
106 if(!tty)
107 tty = "none";
108
109 openlog ( bb_applet_name, 0, LOG_AUTH );
110#endif
111
112 pw = getpwnam ( opt_username );
113 if ( !pw )
114 bb_error_msg_and_die ( "user %s does not exist", opt_username );
115
116 /* Make sure pw->pw_shell is non-NULL. It may be NULL when NEW_USER
117 is a username that is retrieved via NIS (YP), but that doesn't have
118 a default shell listed. */
119 if ( !pw-> pw_shell || !pw->pw_shell [0] )
120 pw-> pw_shell = (char *) DEFAULT_SHELL;
121
122 if ((( cur_uid == 0 ) || correct_password ( pw ))) {
123 log_su_successful(pw->pw_uid, old_user, tty );
124 } else {
125 log_su_failure (pw->pw_uid, old_user, tty );
126 bb_error_msg_and_die ( "incorrect password" );
127 }
128
129#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE )
130 closelog();
131#endif
132
133 if ( !opt_shell && opt_preserve )
134 opt_shell = getenv ( "SHELL" );
135
136 if ( opt_shell && cur_uid && restricted_shell ( pw-> pw_shell )) {
137 /* The user being su'd to has a nonstandard shell, and so is
138 probably a uucp account or has restricted access. Don't
139 compromise the account by allowing access with a standard
140 shell. */
141 fputs ( "using restricted shell\n", stderr );
142 opt_shell = 0;
143 }
144
145 if ( !opt_shell )
146 opt_shell = pw->pw_shell;
147
148 change_identity ( pw );
149 setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw );
150 run_shell ( opt_shell, opt_loginshell, opt_command, (const char**)opt_args
151#ifdef CONFIG_SELINUX
152 , 0
153#endif
154 );
155
156 return EXIT_FAILURE;
157}
diff --git a/busybox/loginutils/sulogin.c b/busybox/loginutils/sulogin.c
new file mode 100644
index 000000000..f21b09571
--- /dev/null
+++ b/busybox/loginutils/sulogin.c
@@ -0,0 +1,158 @@
1/* vi: set sw=4 ts=4: */
2#include <fcntl.h>
3#include <signal.h>
4#include <stdio.h>
5#include <stdlib.h>
6#include <string.h>
7#include <syslog.h>
8#include <unistd.h>
9#include <utmp.h>
10#include <sys/resource.h>
11#include <sys/stat.h>
12#include <sys/time.h>
13#include <sys/types.h>
14#include <ctype.h>
15#include <time.h>
16
17#include "busybox.h"
18
19
20// sulogin defines
21#define SULOGIN_PROMPT "\nGive root password for system maintenance\n" \
22 "(or type Control-D for normal startup):"
23
24static const char *forbid[] = {
25 "ENV",
26 "BASH_ENV",
27 "HOME",
28 "IFS",
29 "PATH",
30 "SHELL",
31 "LD_LIBRARY_PATH",
32 "LD_PRELOAD",
33 "LD_TRACE_LOADED_OBJECTS",
34 "LD_BIND_NOW",
35 "LD_AOUT_LIBRARY_PATH",
36 "LD_AOUT_PRELOAD",
37 "LD_NOWARN",
38 "LD_KEEPDIR",
39 (char *) 0
40};
41
42
43
44static void catchalarm(int junk)
45{
46 exit(EXIT_FAILURE);
47}
48
49
50extern int sulogin_main(int argc, char **argv)
51{
52 char *cp;
53 char *device = (char *) 0;
54 const char *name = "root";
55 int timeout = 0;
56 static char pass[BUFSIZ];
57 struct passwd pwent;
58 struct passwd *pwd;
59 time_t start, now;
60 const char **p;
61#ifdef CONFIG_FEATURE_SHADOWPASSWDS
62 struct spwd *spwd = NULL;
63#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
64
65 openlog("sulogin", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH);
66 if (argc > 1) {
67 if (strncmp(argv[1], "-t", 2) == 0) {
68 if (strcmp(argv[1], "-t") == 0) {
69 if (argc > 2) {
70 timeout = atoi(argv[2]);
71 if (argc > 3) {
72 device = argv[3];
73 }
74 }
75 } else {
76 if (argc > 2) {
77 device = argv[2];
78 }
79 }
80 } else {
81 device = argv[1];
82 }
83 if (device) {
84 close(0);
85 close(1);
86 close(2);
87 if (open(device, O_RDWR) >= 0) {
88 dup(0);
89 dup(0);
90 } else {
91 syslog(LOG_WARNING, "cannot open %s\n", device);
92 exit(EXIT_FAILURE);
93 }
94 }
95 }
96 if (access(bb_path_passwd_file, 0) == -1) {
97 syslog(LOG_WARNING, "No password file\n");
98 bb_error_msg_and_die("No password file\n");
99 }
100 if (!isatty(0) || !isatty(1) || !isatty(2)) {
101 exit(EXIT_FAILURE);
102 }
103
104
105 /* Clear out anything dangerous from the environment */
106 for (p = forbid; *p; p++)
107 unsetenv(*p);
108
109
110 signal(SIGALRM, catchalarm);
111 if (!(pwd = getpwnam(name))) {
112 syslog(LOG_WARNING, "No password entry for `root'\n");
113 bb_error_msg_and_die("No password entry for `root'\n");
114 }
115 pwent = *pwd;
116#ifdef CONFIG_FEATURE_SHADOWPASSWDS
117 spwd = NULL;
118 if (pwd && ((strcmp(pwd->pw_passwd, "x") == 0)
119 || (strcmp(pwd->pw_passwd, "*") == 0))) {
120 endspent();
121 spwd = getspnam(name);
122 if (spwd) {
123 pwent.pw_passwd = spwd->sp_pwdp;
124 }
125 }
126#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
127 while (1) {
128 cp = bb_askpass(timeout, SULOGIN_PROMPT);
129 if (!cp || !*cp) {
130 puts("\n");
131 fflush(stdout);
132 syslog(LOG_INFO, "Normal startup\n");
133 exit(EXIT_SUCCESS);
134 } else {
135 safe_strncpy(pass, cp, sizeof(pass));
136 bzero(cp, strlen(cp));
137 }
138 if (strcmp(pw_encrypt(pass, pwent.pw_passwd), pwent.pw_passwd) == 0) {
139 break;
140 }
141 time(&start);
142 now = start;
143 while (difftime(now, start) < FAIL_DELAY) {
144 sleep(FAIL_DELAY);
145 time(&now);
146 }
147 puts("Login incorrect");
148 fflush(stdout);
149 syslog(LOG_WARNING, "Incorrect root password\n");
150 }
151 bzero(pass, strlen(pass));
152 signal(SIGALRM, SIG_DFL);
153 puts("Entering System Maintenance Mode\n");
154 fflush(stdout);
155 syslog(LOG_INFO, "System Maintenance Mode\n");
156 run_shell(pwent.pw_shell, 1, 0, 0);
157 return (0);
158}
diff --git a/busybox/loginutils/vlock.c b/busybox/loginutils/vlock.c
new file mode 100644
index 000000000..def484ae6
--- /dev/null
+++ b/busybox/loginutils/vlock.c
@@ -0,0 +1,231 @@
1/* vi: set sw=4 ts=4: */
2/*
3 * vlock implementation for busybox
4 *
5 * Copyright (C) 2000 by spoon <spoon@ix.netcom.com>
6 * Written by spoon <spon@ix.netcom.com>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 *
22 */
23
24/* Shoutz to Michael K. Johnson <johnsonm@redhat.com>, author of the
25 * original vlock. I snagged a bunch of his code to write this
26 * minimalistic vlock.
27 */
28/* Fixed by Erik Andersen to do passwords the tinylogin way...
29 * It now works with md5, sha1, etc passwords. */
30
31#include <stdio.h>
32#include <stdlib.h>
33#include <sys/vt.h>
34#include <signal.h>
35#include <string.h>
36#include <unistd.h>
37#include <fcntl.h>
38#include <errno.h>
39#include <sys/ioctl.h>
40#include <termios.h>
41
42#include "busybox.h"
43
44static struct passwd *pw;
45static struct vt_mode ovtm;
46static struct termios oterm;
47static int vfd;
48static int o_lock_all = 0;
49
50#ifdef CONFIG_FEATURE_SHADOWPASSWDS
51static struct spwd *spw;
52
53/* getspuid - get a shadow entry by uid */
54struct spwd *getspuid(uid_t uid)
55{
56 struct spwd *sp;
57 struct passwd *mypw;
58
59 if ((mypw = getpwuid(getuid())) == NULL) {
60 return (NULL);
61 }
62 setspent();
63 while ((sp = getspent()) != NULL) {
64 if (strcmp(mypw->pw_name, sp->sp_namp) == 0)
65 break;
66 }
67 endspent();
68 return (sp);
69}
70#endif
71
72static void release_vt(int signo)
73{
74 if (!o_lock_all)
75 ioctl(vfd, VT_RELDISP, 1);
76 else
77 ioctl(vfd, VT_RELDISP, 0);
78}
79
80static void acquire_vt(int signo)
81{
82 ioctl(vfd, VT_RELDISP, VT_ACKACQ);
83}
84
85static void restore_terminal(void)
86{
87 ioctl(vfd, VT_SETMODE, &ovtm);
88 tcsetattr(STDIN_FILENO, TCSANOW, &oterm);
89}
90
91extern int vlock_main(int argc, char **argv)
92{
93 sigset_t sig;
94 struct sigaction sa;
95 struct vt_mode vtm;
96 int times = 0;
97 struct termios term;
98
99 if (argc > 2) {
100 bb_show_usage();
101 }
102
103 if (argc == 2) {
104 if (strncmp(argv[1], "-a", 2)) {
105 bb_show_usage();
106 } else {
107 o_lock_all = 1;
108 }
109 }
110
111 if ((pw = getpwuid(getuid())) == NULL) {
112 bb_error_msg_and_die("no password for uid %d\n", getuid());
113 }
114#ifdef CONFIG_FEATURE_SHADOWPASSWDS
115 if ((strcmp(pw->pw_passwd, "x") == 0)
116 || (strcmp(pw->pw_passwd, "*") == 0)) {
117
118 if ((spw = getspuid(getuid())) == NULL) {
119 bb_error_msg_and_die("could not read shadow password for uid %d: %s\n",
120 getuid(), strerror(errno));
121 }
122 if (spw->sp_pwdp) {
123 pw->pw_passwd = spw->sp_pwdp;
124 }
125 }
126#endif /* CONFIG_FEATURE_SHADOWPASSWDS */
127 if (pw->pw_passwd[0] == '!' || pw->pw_passwd[0] == '*') {
128 bb_error_msg_and_die("Account disabled for uid %d\n", getuid());
129 }
130
131 /* we no longer need root privs */
132 setuid(getuid());
133 setgid(getgid());
134
135 if ((vfd = open("/dev/tty", O_RDWR)) < 0) {
136 bb_error_msg_and_die("/dev/tty");
137 };
138
139 if (ioctl(vfd, VT_GETMODE, &vtm) < 0) {
140 bb_error_msg_and_die("/dev/tty");
141 };
142
143 /* mask a bunch of signals */
144 sigprocmask(SIG_SETMASK, NULL, &sig);
145 sigdelset(&sig, SIGUSR1);
146 sigdelset(&sig, SIGUSR2);
147 sigaddset(&sig, SIGTSTP);
148 sigaddset(&sig, SIGTTIN);
149 sigaddset(&sig, SIGTTOU);
150 sigaddset(&sig, SIGHUP);
151 sigaddset(&sig, SIGCHLD);
152 sigaddset(&sig, SIGQUIT);
153 sigaddset(&sig, SIGINT);
154
155 sigemptyset(&(sa.sa_mask));
156 sa.sa_flags = SA_RESTART;
157 sa.sa_handler = release_vt;
158 sigaction(SIGUSR1, &sa, NULL);
159 sa.sa_handler = acquire_vt;
160 sigaction(SIGUSR2, &sa, NULL);
161
162 /* need to handle some signals so that we don't get killed by them */
163 sa.sa_handler = SIG_IGN;
164 sigaction(SIGHUP, &sa, NULL);
165 sigaction(SIGQUIT, &sa, NULL);
166 sigaction(SIGINT, &sa, NULL);
167 sigaction(SIGTSTP, &sa, NULL);
168
169 ovtm = vtm;
170 vtm.mode = VT_PROCESS;
171 vtm.relsig = SIGUSR1;
172 vtm.acqsig = SIGUSR2;
173 ioctl(vfd, VT_SETMODE, &vtm);
174
175 tcgetattr(STDIN_FILENO, &oterm);
176 term = oterm;
177 term.c_iflag &= ~BRKINT;
178 term.c_iflag |= IGNBRK;
179 term.c_lflag &= ~ISIG;
180 term.c_lflag &= ~(ECHO | ECHOCTL);
181 tcsetattr(STDIN_FILENO, TCSANOW, &term);
182
183 do {
184 char *pass, *crypt_pass;
185 char prompt[100];
186
187 if (o_lock_all) {
188 printf("All Virtual Consoles locked.\n");
189 } else {
190 printf("This Virtual Console locked.\n");
191 }
192 fflush(stdout);
193
194 snprintf(prompt, 100, "%s's password: ", pw->pw_name);
195
196 if ((pass = bb_askpass(0, prompt)) == NULL) {
197 restore_terminal();
198 bb_perror_msg_and_die("password");
199 }
200
201 crypt_pass = pw_encrypt(pass, pw->pw_passwd);
202 if (strncmp(crypt_pass, pw->pw_passwd, sizeof(crypt_pass)) == 0) {
203 memset(pass, 0, strlen(pass));
204 memset(crypt_pass, 0, strlen(crypt_pass));
205 restore_terminal();
206 return 0;
207 }
208 memset(pass, 0, strlen(pass));
209 memset(crypt_pass, 0, strlen(crypt_pass));
210
211 if (isatty(STDIN_FILENO) == 0) {
212 restore_terminal();
213 bb_perror_msg_and_die("isatty");
214 }
215
216 sleep(++times);
217 printf("Password incorrect.\n");
218 if (times >= 3) {
219 sleep(15);
220 times = 2;
221 }
222 } while (1);
223}
224
225/*
226Local Variables:
227c-file-style: "linux"
228c-basic-offset: 4
229tab-width: 4
230End:
231*/