diff options
author | nobody <nobody@localhost> | 2004-10-13 09:42:10 +0000 |
---|---|---|
committer | nobody <nobody@localhost> | 2004-10-13 09:42:10 +0000 |
commit | 8c59a0bf0e9e2d87b0ff273ea3f0bf05bbbf6373 (patch) | |
tree | 1826706cd4fd009fcd14f4f8021005ec8ec0fa59 /busybox/loginutils | |
download | busybox-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.in | 161 | ||||
-rw-r--r-- | busybox/loginutils/Makefile | 32 | ||||
-rw-r--r-- | busybox/loginutils/Makefile.in | 57 | ||||
-rw-r--r-- | busybox/loginutils/addgroup.c | 171 | ||||
-rw-r--r-- | busybox/loginutils/adduser.c | 314 | ||||
-rw-r--r-- | busybox/loginutils/delgroup.c | 62 | ||||
-rw-r--r-- | busybox/loginutils/delline.c | 113 | ||||
-rw-r--r-- | busybox/loginutils/deluser.c | 68 | ||||
-rw-r--r-- | busybox/loginutils/getty.c | 1020 | ||||
-rw-r--r-- | busybox/loginutils/login.c | 486 | ||||
-rw-r--r-- | busybox/loginutils/passwd.c | 400 | ||||
-rw-r--r-- | busybox/loginutils/su.c | 157 | ||||
-rw-r--r-- | busybox/loginutils/sulogin.c | 158 | ||||
-rw-r--r-- | busybox/loginutils/vlock.c | 231 |
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 | |||
6 | menu "Login/Password Management Utilities" | ||
7 | |||
8 | config 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 | |||
30 | config CONFIG_ADDGROUP | ||
31 | bool "addgroup" | ||
32 | default n | ||
33 | help | ||
34 | Utility for creating a new group account. | ||
35 | |||
36 | config CONFIG_DELGROUP | ||
37 | bool "delgroup" | ||
38 | default n | ||
39 | help | ||
40 | Utility for deleting a group account. | ||
41 | |||
42 | config CONFIG_ADDUSER | ||
43 | bool "adduser" | ||
44 | default n | ||
45 | help | ||
46 | Utility for creating a new user account. | ||
47 | |||
48 | config CONFIG_DELUSER | ||
49 | bool "deluser" | ||
50 | default n | ||
51 | help | ||
52 | Utility for deleting a user account. | ||
53 | |||
54 | config 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 | |||
60 | config 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 | |||
69 | config 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 | |||
79 | config 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 | |||
88 | config 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 | |||
101 | config 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 | |||
112 | config 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 | |||
119 | config 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 | |||
129 | comment "Common options for adduser, deluser, login, su" | ||
130 | depends on CONFIG_ADDUSER || CONFIG_DELUSER || CONFIG_LOGIN || CONFIG_SU | ||
131 | |||
132 | config 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 | |||
141 | config 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 | |||
160 | endmenu | ||
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 | |||
20 | top_srcdir=.. | ||
21 | top_builddir=.. | ||
22 | srcdir=$(top_srcdir)/loginutils | ||
23 | LOGINUTILS_DIR:=./ | ||
24 | include $(top_builddir)/Rules.mak | ||
25 | include $(top_builddir)/.config | ||
26 | include Makefile.in | ||
27 | all: $(libraries-y) | ||
28 | -include $(top_builddir)/.depend | ||
29 | |||
30 | clean: | ||
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 | |||
20 | LOGINUTILS_AR:=loginutils.a | ||
21 | ifndef LOGINUTILS_DIR | ||
22 | LOGINUTILS_DIR:=$(top_builddir)/loginutils/ | ||
23 | endif | ||
24 | srcdir=$(top_srcdir)/loginutils | ||
25 | |||
26 | LOGINUTILS-y:= | ||
27 | LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o | ||
28 | LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o | ||
29 | LOGINUTILS-$(CONFIG_GETTY) += getty.o | ||
30 | LOGINUTILS-$(CONFIG_LOGIN) += login.o | ||
31 | LOGINUTILS-$(CONFIG_PASSWD) += passwd.o | ||
32 | LOGINUTILS-$(CONFIG_SU) += su.o | ||
33 | LOGINUTILS-$(CONFIG_SULOGIN) += sulogin.o | ||
34 | LOGINUTILS-$(CONFIG_VLOCK) += vlock.o | ||
35 | LOGINUTILS-$(CONFIG_DELUSER) += deluser.o | ||
36 | LOGINUTILS-$(CONFIG_DELGROUP) += delgroup.o | ||
37 | |||
38 | libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR) | ||
39 | |||
40 | needcrypt-y:= | ||
41 | needcrypt-$(CONFIG_LOGIN) := y | ||
42 | needcrypt-$(CONFIG_PASSWD) := y | ||
43 | needcrypt-$(CONFIG_SU) := y | ||
44 | needcrypt-$(CONFIG_SULOGIN) := y | ||
45 | needcrypt-$(CONFIG_VLOCK) := y | ||
46 | |||
47 | |||
48 | ifeq ($(needcrypt-y),y) | ||
49 | LIBRARIES += -lcrypt | ||
50 | endif | ||
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? */ | ||
44 | static 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 */ | ||
49 | static 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 */ | ||
86 | static 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 | ||
126 | static 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 | ||
133 | extern 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 | * ________________________________________________________________________ */ | ||
143 | int 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 | |||
45 | typedef struct { | ||
46 | uid_t u; | ||
47 | gid_t g; | ||
48 | } Id; | ||
49 | |||
50 | /* data _____________________________ */ | ||
51 | |||
52 | /* defaults : should this be in an external file? */ | ||
53 | static const char default_passwd[] = "x"; | ||
54 | static const char default_gecos[] = "Linux User,,,"; | ||
55 | static const char default_home_prefix[] = "/home"; | ||
56 | |||
57 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
58 | /* shadow in use? */ | ||
59 | static int shadow_enabled = 0; | ||
60 | #endif | ||
61 | |||
62 | /* remix */ | ||
63 | /* EDR recoded such that the uid may be passed in *p */ | ||
64 | static 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 | |||
116 | static 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 | |||
125 | static void passwd_wrapper(const char *login) __attribute__ ((noreturn)); | ||
126 | |||
127 | static 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 */ | ||
135 | static 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 | ||
231 | static inline void if_i_am_not_root(void) | ||
232 | #else | ||
233 | void 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 | * ________________________________________________________________________ */ | ||
253 | int 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 | ||
35 | extern int del_line_matching(const char *login, const char *filename); | ||
36 | #endif | ||
37 | |||
38 | int 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 */ | ||
34 | typedef 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. */ | ||
42 | static 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 */ | ||
67 | int 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 | |||
34 | static const char deluser_format[]="%s: User could not be removed from %s"; | ||
35 | |||
36 | int 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 | ||
51 | extern 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 | |||
132 | struct 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 | |||
154 | struct 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 | |||
164 | struct 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 | ||
173 | struct Speedtab { | ||
174 | long speed; | ||
175 | int code; | ||
176 | }; | ||
177 | |||
178 | static 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 | |||
217 | static void parse_args(int argc, char **argv, struct options *op); | ||
218 | static void parse_speeds(struct options *op, char *arg); | ||
219 | static void open_tty(char *tty, struct termio *tp, int local); | ||
220 | static void termio_init(struct termio *tp, int speed, struct options *op); | ||
221 | static void auto_baud(struct termio *tp); | ||
222 | static void do_prompt(struct options *op, struct termio *tp); | ||
223 | static void next_speed(struct termio *tp, struct options *op); | ||
224 | static char *get_logname(struct options *op, struct chardata *cp, | ||
225 | |||
226 | struct termio *tp); | ||
227 | static void termio_final(struct options *op, struct termio *tp, | ||
228 | |||
229 | struct chardata *cp); | ||
230 | static int caps_lock(const char *s); | ||
231 | static int bcode(char *s); | ||
232 | static void error(const char *fmt, ...) __attribute__ ((noreturn)); | ||
233 | |||
234 | #ifdef CONFIG_FEATURE_U_W_TMP | ||
235 | static 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. */ | ||
241 | static char *fakehost = NULL; | ||
242 | |||
243 | /* ... */ | ||
244 | #ifdef DEBUGGING | ||
245 | #define debug(s) fprintf(dbf,s); fflush(dbf) | ||
246 | #define DEBUGTERM "/dev/ttyp0" | ||
247 | FILE *dbf; | ||
248 | #else | ||
249 | #define debug(s) /* nothing */ | ||
250 | #endif | ||
251 | |||
252 | int 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 | |||
378 | static 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 | |||
470 | static 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 */ | ||
488 | static 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 } */ | ||
548 | static 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 | |||
656 | static 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 */ | ||
695 | static 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 */ | ||
756 | static 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 */ | ||
765 | static 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 */ | ||
777 | static 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 */ | ||
885 | static 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 */ | ||
948 | static 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 */ | ||
962 | static 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 | */ | ||
982 | static 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 | ||
28 | static void checkutmp(int picky); | ||
29 | static void setutmp(const char *name, const char *line); | ||
30 | /* Stuff global to this file */ | ||
31 | struct 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 | |||
40 | static int check_nologin ( int amroot ); | ||
41 | |||
42 | #if defined CONFIG_FEATURE_SECURETTY | ||
43 | static int check_tty ( const char *tty ); | ||
44 | |||
45 | #else | ||
46 | static inline int check_tty ( const char *tty ) { return 1; } | ||
47 | |||
48 | #endif | ||
49 | |||
50 | static int is_my_tty ( const char *tty ); | ||
51 | static int login_prompt ( char *buf_name ); | ||
52 | static void motd ( void ); | ||
53 | |||
54 | |||
55 | static void alarm_handler ( int sig ) | ||
56 | { | ||
57 | fprintf (stderr, "\nLogin timed out after %d seconds.\n", TIMEOUT ); | ||
58 | exit ( EXIT_SUCCESS ); | ||
59 | } | ||
60 | |||
61 | |||
62 | extern 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 | |||
198 | auth_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 | |||
293 | static 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 | |||
320 | static 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 | |||
345 | static 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 */ | ||
376 | static 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 | |||
390 | static 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 | |||
425 | static 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 | |||
472 | static 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 | |||
17 | static char crypt_passwd[128]; | ||
18 | |||
19 | static int create_backup(const char *backup, FILE * fp); | ||
20 | static int new_password(const struct passwd *pw, int amroot, int algo); | ||
21 | static void set_filesize_limit(int blocks); | ||
22 | |||
23 | |||
24 | int 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 | |||
34 | extern 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 | |||
134 | extern 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 | |||
250 | static 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 | |||
289 | static 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 | |||
304 | static 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 | |||
319 | static 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 | |||
394 | static 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 ) | ||
34 | static 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 | |||
59 | int 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 | |||
24 | static 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 | |||
44 | static void catchalarm(int junk) | ||
45 | { | ||
46 | exit(EXIT_FAILURE); | ||
47 | } | ||
48 | |||
49 | |||
50 | extern 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 | |||
44 | static struct passwd *pw; | ||
45 | static struct vt_mode ovtm; | ||
46 | static struct termios oterm; | ||
47 | static int vfd; | ||
48 | static int o_lock_all = 0; | ||
49 | |||
50 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
51 | static struct spwd *spw; | ||
52 | |||
53 | /* getspuid - get a shadow entry by uid */ | ||
54 | struct 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 | |||
72 | static 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 | |||
80 | static void acquire_vt(int signo) | ||
81 | { | ||
82 | ioctl(vfd, VT_RELDISP, VT_ACKACQ); | ||
83 | } | ||
84 | |||
85 | static void restore_terminal(void) | ||
86 | { | ||
87 | ioctl(vfd, VT_SETMODE, &ovtm); | ||
88 | tcsetattr(STDIN_FILENO, TCSANOW, &oterm); | ||
89 | } | ||
90 | |||
91 | extern 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 | /* | ||
226 | Local Variables: | ||
227 | c-file-style: "linux" | ||
228 | c-basic-offset: 4 | ||
229 | tab-width: 4 | ||
230 | End: | ||
231 | */ | ||