diff options
| author | Robert Griebl <griebl@gmx.de> | 2002-06-04 20:45:46 +0000 |
|---|---|---|
| committer | Robert Griebl <griebl@gmx.de> | 2002-06-04 20:45:46 +0000 |
| commit | 1fca558799350fb6caff97f55aa6d1237b08fccb (patch) | |
| tree | bd211c50ca4a34fd4c6317696c62564fb77171e9 /loginutils | |
| parent | ea1a63a2011a44b143cc46c7d80a8152f5358e24 (diff) | |
| download | busybox-w32-1fca558799350fb6caff97f55aa6d1237b08fccb.tar.gz busybox-w32-1fca558799350fb6caff97f55aa6d1237b08fccb.tar.bz2 busybox-w32-1fca558799350fb6caff97f55aa6d1237b08fccb.zip | |
Bigger patch for (partial) tinylogin integration
- Made a new dir loginutils
- Moved all applets from pwd_grp to loginutils
- Added new applets su.c login.c to loginutils
- Made a new dir libpwdgrp
- Moved everything from pwd_grp/libpwd_grp there
- Added shadow.c to libpwdgrp
- Removed dir pwd_grp
- Added usage strings for login and su to usage.h
- Changed main Makefile to reflect the dir rearrangements
[Parts of this patch may overlap with my other two patches]
Diffstat (limited to 'loginutils')
| -rw-r--r-- | loginutils/Makefile | 30 | ||||
| -rw-r--r-- | loginutils/Makefile.in | 45 | ||||
| -rw-r--r-- | loginutils/addgroup.c | 168 | ||||
| -rw-r--r-- | loginutils/adduser.c | 352 | ||||
| -rw-r--r-- | loginutils/config.in | 25 | ||||
| -rw-r--r-- | loginutils/deluser.c | 183 | ||||
| -rw-r--r-- | loginutils/getty.c | 1157 | ||||
| -rw-r--r-- | loginutils/login.c | 459 | ||||
| -rw-r--r-- | loginutils/su.c | 167 | ||||
| -rw-r--r-- | loginutils/tinylogin.c | 209 | ||||
| -rw-r--r-- | loginutils/tinylogin.h | 10 |
11 files changed, 2805 insertions, 0 deletions
diff --git a/loginutils/Makefile b/loginutils/Makefile new file mode 100644 index 000000000..09b368afc --- /dev/null +++ b/loginutils/Makefile | |||
| @@ -0,0 +1,30 @@ | |||
| 1 | # Makefile for busybox | ||
| 2 | # | ||
| 3 | # Copyright (C) 1999-2002 Erik Andersen <andersee@debian.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 | TOPDIR:= ../ | ||
| 21 | LOGINUTILS_DIR:=./ | ||
| 22 | include $(TOPDIR).config | ||
| 23 | include $(TOPDIR)Rules.mak | ||
| 24 | include Makefile.in | ||
| 25 | all: $(libraries-y) | ||
| 26 | -include $(TOPDIR).depend | ||
| 27 | |||
| 28 | clean: | ||
| 29 | rm -f *.o *.a $(AR_TARGET) | ||
| 30 | |||
diff --git a/loginutils/Makefile.in b/loginutils/Makefile.in new file mode 100644 index 000000000..adee35baa --- /dev/null +++ b/loginutils/Makefile.in | |||
| @@ -0,0 +1,45 @@ | |||
| 1 | # Makefile for busybox | ||
| 2 | # | ||
| 3 | # Copyright (C) 1999-2002 Erik Andersen <andersee@debian.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:=$(TOPDIR)loginutils/ | ||
| 23 | endif | ||
| 24 | |||
| 25 | LOGINUTILS-y:= | ||
| 26 | LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o | ||
| 27 | LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o | ||
| 28 | LOGINUTILS-$(CONFIG_DELUSER) += deluser.o | ||
| 29 | LOGINUTILS-$(CONFIG_GETTY) += getty.o | ||
| 30 | LOGINUTILS-$(CONFIG_LOGIN) += login.o tinylogin.o | ||
| 31 | LOGINUTILS-$(CONFIG_SU) += su.o tinylogin.o | ||
| 32 | |||
| 33 | libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR) | ||
| 34 | |||
| 35 | needcrypt-y:= | ||
| 36 | needcrypt-$(CONFIG_LOGIN) := y | ||
| 37 | needcrypt-$(CONFIG_SU) := y | ||
| 38 | |||
| 39 | ifeq ($(needcrypt-y),y) | ||
| 40 | libraries-y +=-lcrypt | ||
| 41 | endif | ||
| 42 | |||
| 43 | $(LOGINUTILS_DIR)$(LOGINUTILS_AR): $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y)) | ||
| 44 | $(AR) -ro $@ $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y)) | ||
| 45 | |||
diff --git a/loginutils/addgroup.c b/loginutils/addgroup.c new file mode 100644 index 000000000..e04a8d784 --- /dev/null +++ b/loginutils/addgroup.c | |||
| @@ -0,0 +1,168 @@ | |||
| 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 | #define GROUP_FILE "/etc/group" | ||
| 39 | #define SHADOW_FILE "/etc/gshadow" | ||
| 40 | |||
| 41 | |||
| 42 | /* structs __________________________ */ | ||
| 43 | |||
| 44 | /* data _____________________________ */ | ||
| 45 | |||
| 46 | /* defaults : should this be in an external file? */ | ||
| 47 | static const char default_passwd[] = "x"; | ||
| 48 | |||
| 49 | |||
| 50 | /* make sure gr_name isn't taken, make sure gid is kosher | ||
| 51 | * return 1 on failure */ | ||
| 52 | static int group_study(const char *filename, struct group *g) | ||
| 53 | { | ||
| 54 | FILE *etc_group; | ||
| 55 | gid_t desired; | ||
| 56 | |||
| 57 | struct group *grp; | ||
| 58 | const int max = 65000; | ||
| 59 | |||
| 60 | etc_group = xfopen(filename, "r"); | ||
| 61 | |||
| 62 | /* make sure gr_name isn't taken, make sure gid is kosher */ | ||
| 63 | desired = g->gr_gid; | ||
| 64 | while ((grp = fgetgrent(etc_group))) { | ||
| 65 | if ((strcmp(grp->gr_name, g->gr_name)) == 0) { | ||
| 66 | error_msg_and_die("%s: group already in use\n", g->gr_name); | ||
| 67 | } | ||
| 68 | if ((desired) && grp->gr_gid == desired) { | ||
| 69 | error_msg_and_die("%d: gid has already been allocated\n", | ||
| 70 | desired); | ||
| 71 | } | ||
| 72 | if ((grp->gr_gid > g->gr_gid) && (grp->gr_gid < max)) { | ||
| 73 | g->gr_gid = grp->gr_gid; | ||
| 74 | } | ||
| 75 | } | ||
| 76 | fclose(etc_group); | ||
| 77 | |||
| 78 | /* gid */ | ||
| 79 | if (desired) { | ||
| 80 | g->gr_gid = desired; | ||
| 81 | } else { | ||
| 82 | g->gr_gid++; | ||
| 83 | } | ||
| 84 | /* return 1; */ | ||
| 85 | return 0; | ||
| 86 | } | ||
| 87 | |||
| 88 | /* append a new user to the passwd file */ | ||
| 89 | static int addgroup(const char *filename, char *group, gid_t gid) | ||
| 90 | { | ||
| 91 | FILE *etc_group; | ||
| 92 | |||
| 93 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 94 | FILE *etc_gshadow; | ||
| 95 | char *gshadow = SHADOW_FILE; | ||
| 96 | #endif | ||
| 97 | |||
| 98 | struct group gr; | ||
| 99 | |||
| 100 | /* group:passwd:gid:userlist */ | ||
| 101 | static const char entryfmt[] = "%s:%s:%d:%s\n"; | ||
| 102 | |||
| 103 | /* make sure gid and group haven't already been allocated */ | ||
| 104 | gr.gr_gid = gid; | ||
| 105 | gr.gr_name = group; | ||
| 106 | if (group_study(filename, &gr)) | ||
| 107 | return 1; | ||
| 108 | |||
| 109 | /* add entry to group */ | ||
| 110 | etc_group = xfopen(filename, "a"); | ||
| 111 | |||
| 112 | fprintf(etc_group, entryfmt, group, default_passwd, gr.gr_gid, ""); | ||
| 113 | fclose(etc_group); | ||
| 114 | |||
| 115 | |||
| 116 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 117 | /* add entry to gshadow if necessary */ | ||
| 118 | if (access(gshadow, F_OK|W_OK) == 0) { | ||
| 119 | etc_gshadow = xfopen(gshadow, "a"); | ||
| 120 | fprintf(etc_gshadow, "%s:!::\n", group); | ||
| 121 | fclose(etc_gshadow); | ||
| 122 | } | ||
| 123 | #endif | ||
| 124 | |||
| 125 | /* return 1; */ | ||
| 126 | return 0; | ||
| 127 | } | ||
| 128 | |||
| 129 | /* | ||
| 130 | * addgroup will take a login_name as its first parameter. | ||
| 131 | * | ||
| 132 | * gid | ||
| 133 | * | ||
| 134 | * can be customized via command-line parameters. | ||
| 135 | * ________________________________________________________________________ */ | ||
| 136 | int addgroup_main(int argc, char **argv) | ||
| 137 | { | ||
| 138 | int opt; | ||
| 139 | char *group; | ||
| 140 | gid_t gid = 0; | ||
| 141 | |||
| 142 | /* get remaining args */ | ||
| 143 | while ((opt = getopt (argc, argv, "g:")) != -1) | ||
| 144 | switch (opt) { | ||
| 145 | case 'g': | ||
| 146 | gid = strtol(optarg, NULL, 10); | ||
| 147 | break; | ||
| 148 | default: | ||
| 149 | show_usage(); | ||
| 150 | break; | ||
| 151 | } | ||
| 152 | |||
| 153 | if (optind >= argc) { | ||
| 154 | show_usage(); | ||
| 155 | } else { | ||
| 156 | group = argv[optind]; | ||
| 157 | } | ||
| 158 | |||
| 159 | if (geteuid() != 0) { | ||
| 160 | error_msg_and_die | ||
| 161 | ("Only root may add a group to the system."); | ||
| 162 | } | ||
| 163 | |||
| 164 | /* werk */ | ||
| 165 | return addgroup(GROUP_FILE, group, gid); | ||
| 166 | } | ||
| 167 | |||
| 168 | /* $Id: addgroup.c,v 1.1 2002/06/04 20:45:05 sandman Exp $ */ | ||
diff --git a/loginutils/adduser.c b/loginutils/adduser.c new file mode 100644 index 000000000..66fcaa23f --- /dev/null +++ b/loginutils/adduser.c | |||
| @@ -0,0 +1,352 @@ | |||
| 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 | #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 <time.h> | ||
| 31 | #include <unistd.h> | ||
| 32 | #include <sys/param.h> | ||
| 33 | #include <sys/stat.h> | ||
| 34 | #include <sys/types.h> | ||
| 35 | #include "busybox.h" | ||
| 36 | #include "pwd.h" | ||
| 37 | #include "grp.h" | ||
| 38 | |||
| 39 | #define PASSWD_FILE "/etc/passwd" | ||
| 40 | #define SHADOW_FILE "/etc/shadow" | ||
| 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 | static const char default_shell[] = "/bin/sh"; | ||
| 57 | |||
| 58 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 59 | |||
| 60 | #include "shadow.h" | ||
| 61 | |||
| 62 | /* shadow in use? */ | ||
| 63 | static int shadow_enabled = 0; | ||
| 64 | #endif | ||
| 65 | |||
| 66 | /* remix */ | ||
| 67 | /* EDR recoded such that the uid may be passed in *p */ | ||
| 68 | static int passwd_study(const char *filename, struct passwd *p) | ||
| 69 | { | ||
| 70 | struct passwd *pw; | ||
| 71 | FILE *passwd; | ||
| 72 | |||
| 73 | const int min = 500; | ||
| 74 | const int max = 65000; | ||
| 75 | |||
| 76 | passwd = wfopen(filename, "r"); | ||
| 77 | if (!passwd) | ||
| 78 | return 4; | ||
| 79 | |||
| 80 | /* EDR if uid is out of bounds, set to min */ | ||
| 81 | if ((p->pw_uid > max) || (p->pw_uid < min)) | ||
| 82 | p->pw_uid = min; | ||
| 83 | |||
| 84 | /* stuff to do: | ||
| 85 | * make sure login isn't taken; | ||
| 86 | * find free uid and gid; | ||
| 87 | */ | ||
| 88 | while ((pw = fgetpwent(passwd))) { | ||
| 89 | if (strcmp(pw->pw_name, p->pw_name) == 0) { | ||
| 90 | /* return 0; */ | ||
| 91 | return 1; | ||
| 92 | } | ||
| 93 | if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max) | ||
| 94 | && (pw->pw_uid >= min)) { | ||
| 95 | p->pw_uid = pw->pw_uid + 1; | ||
| 96 | } | ||
| 97 | } | ||
| 98 | |||
| 99 | /* EDR check for an already existing gid */ | ||
| 100 | while (getgrgid(p->pw_uid) != NULL) | ||
| 101 | p->pw_uid++; | ||
| 102 | |||
| 103 | /* EDR also check for an existing group definition */ | ||
| 104 | if (getgrnam(p->pw_name) != NULL) | ||
| 105 | return 3; | ||
| 106 | |||
| 107 | /* EDR bounds check */ | ||
| 108 | if ((p->pw_uid > max) || (p->pw_uid < min)) | ||
| 109 | return 2; | ||
| 110 | |||
| 111 | /* EDR create new gid always = uid */ | ||
| 112 | p->pw_gid = p->pw_uid; | ||
| 113 | |||
| 114 | /* return 1; */ | ||
| 115 | return 0; | ||
| 116 | } | ||
| 117 | |||
| 118 | static void addgroup_wrapper(const char *login, gid_t gid) | ||
| 119 | { | ||
| 120 | int argc = 3; | ||
| 121 | const char *argv0_save; | ||
| 122 | char group_id[8]; | ||
| 123 | char group_name[32]; | ||
| 124 | char *argv[] = { group_name, "-g", group_id }; | ||
| 125 | |||
| 126 | argv0_save = applet_name; | ||
| 127 | applet_name = "addgroup"; | ||
| 128 | safe_strncpy(group_name, login, 32); | ||
| 129 | sprintf(group_id, "%d", gid); | ||
| 130 | addgroup_main(argc, argv); | ||
| 131 | applet_name = argv0_save; | ||
| 132 | } | ||
| 133 | |||
| 134 | static void passwd_wrapper(const char *login) | ||
| 135 | { | ||
| 136 | static const char prog[] = "passwd"; | ||
| 137 | execlp(prog, prog, login, NULL); | ||
| 138 | error_msg_and_die("Failed to execute 'passwd', you must set the password for '%s' manually", login); | ||
| 139 | } | ||
| 140 | |||
| 141 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 142 | /* | ||
| 143 | * pwd_to_spwd - create entries for new spwd structure | ||
| 144 | * | ||
| 145 | * pwd_to_spwd() creates a new (struct spwd) containing the | ||
| 146 | * information in the pointed-to (struct passwd). | ||
| 147 | */ | ||
| 148 | #define DAY (24L*3600L) | ||
| 149 | #define WEEK (7*DAY) | ||
| 150 | #define SCALE DAY | ||
| 151 | static struct spwd *pwd_to_spwd(const struct passwd *pw) | ||
| 152 | { | ||
| 153 | static struct spwd sp; | ||
| 154 | |||
| 155 | /* | ||
| 156 | * Nice, easy parts first. The name and passwd map directly | ||
| 157 | * from the old password structure to the new one. | ||
| 158 | */ | ||
| 159 | sp.sp_namp = pw->pw_name; | ||
| 160 | sp.sp_pwdp = pw->pw_passwd; | ||
| 161 | |||
| 162 | /* | ||
| 163 | * Defaults used if there is no pw_age information. | ||
| 164 | */ | ||
| 165 | sp.sp_min = 0; | ||
| 166 | sp.sp_max = (10000L * DAY) / SCALE; | ||
| 167 | sp.sp_lstchg = time((time_t *) 0) / SCALE; | ||
| 168 | |||
| 169 | /* | ||
| 170 | * These fields have no corresponding information in the password | ||
| 171 | * file. They are set to uninitialized values. | ||
| 172 | */ | ||
| 173 | sp.sp_warn = -1; | ||
| 174 | sp.sp_expire = -1; | ||
| 175 | sp.sp_inact = -1; | ||
| 176 | sp.sp_flag = -1; | ||
| 177 | |||
| 178 | return &sp; | ||
| 179 | } | ||
| 180 | #endif | ||
| 181 | |||
| 182 | /* putpwent(3) remix */ | ||
| 183 | static int adduser(const char *filename, struct passwd *p) | ||
| 184 | { | ||
| 185 | FILE *passwd; | ||
| 186 | int r; | ||
| 187 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 188 | FILE *shadow; | ||
| 189 | struct spwd *sp; | ||
| 190 | #endif | ||
| 191 | |||
| 192 | /* make sure everything is kosher and setup uid && gid */ | ||
| 193 | passwd = wfopen(filename, "a"); | ||
| 194 | if (passwd == NULL) { | ||
| 195 | /* return -1; */ | ||
| 196 | return 1; | ||
| 197 | } | ||
| 198 | fseek(passwd, 0, SEEK_END); | ||
| 199 | |||
| 200 | /* if (passwd_study(filename, p) == 0) { */ | ||
| 201 | r = passwd_study(filename, p); | ||
| 202 | if (r) { | ||
| 203 | if (r == 1) | ||
| 204 | error_msg("%s: login already in use", p->pw_name); | ||
| 205 | else if (r == 2) | ||
| 206 | error_msg("illegal uid or no uids left"); | ||
| 207 | else if (r == 3) | ||
| 208 | error_msg("group name %s already in use", p->pw_name); | ||
| 209 | else | ||
| 210 | error_msg("generic error."); | ||
| 211 | /* return -1; */ | ||
| 212 | return 1; | ||
| 213 | } | ||
| 214 | |||
| 215 | /* add to passwd */ | ||
| 216 | if (putpwent(p, passwd) == -1) { | ||
| 217 | /* return -1; */ | ||
| 218 | return 1; | ||
| 219 | } | ||
| 220 | fclose(passwd); | ||
| 221 | |||
| 222 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 223 | /* add to shadow if necessary */ | ||
| 224 | if (shadow_enabled) { | ||
| 225 | shadow = wfopen(SHADOW_FILE, "a"); | ||
| 226 | if (shadow == NULL) { | ||
| 227 | /* return -1; */ | ||
| 228 | return 1; | ||
| 229 | } | ||
| 230 | fseek(shadow, 0, SEEK_END); | ||
| 231 | sp = pwd_to_spwd(p); | ||
| 232 | sp->sp_max = 99999; /* debianish */ | ||
| 233 | sp->sp_warn = 7; | ||
| 234 | fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n", | ||
| 235 | sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max, | ||
| 236 | sp->sp_warn); | ||
| 237 | fclose(shadow); | ||
| 238 | } | ||
| 239 | #endif | ||
| 240 | |||
| 241 | /* add to group */ | ||
| 242 | /* addgroup should be responsible for dealing w/ gshadow */ | ||
| 243 | addgroup_wrapper(p->pw_name, p->pw_gid); | ||
| 244 | |||
| 245 | /* Clear the umask for this process so it doesn't | ||
| 246 | * * screw up the permissions on the mkdir and chown. */ | ||
| 247 | umask(0); | ||
| 248 | |||
| 249 | /* mkdir */ | ||
| 250 | if (mkdir(p->pw_dir, 0755)) { | ||
| 251 | perror_msg("%s", p->pw_dir); | ||
| 252 | } | ||
| 253 | /* Set the owner and group so it is owned by the new user. */ | ||
| 254 | if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) { | ||
| 255 | perror_msg("%s", p->pw_dir); | ||
| 256 | } | ||
| 257 | /* Now fix up the permissions to 2755. Can't do it before now | ||
| 258 | * since chown will clear the setgid bit */ | ||
| 259 | if (chmod(p->pw_dir, 02755)) { | ||
| 260 | perror_msg("%s", p->pw_dir); | ||
| 261 | } | ||
| 262 | /* interactively set passwd */ | ||
| 263 | passwd_wrapper(p->pw_name); | ||
| 264 | |||
| 265 | return 0; | ||
| 266 | } | ||
| 267 | |||
| 268 | |||
| 269 | /* return current uid (root is always uid == 0, right?) */ | ||
| 270 | static inline uid_t i_am_not_root(void) | ||
| 271 | { | ||
| 272 | return geteuid(); | ||
| 273 | } | ||
| 274 | |||
| 275 | /* | ||
| 276 | * adduser will take a login_name as its first parameter. | ||
| 277 | * | ||
| 278 | * home | ||
| 279 | * shell | ||
| 280 | * gecos | ||
| 281 | * | ||
| 282 | * can be customized via command-line parameters. | ||
| 283 | * ________________________________________________________________________ */ | ||
| 284 | int adduser_main(int argc, char **argv) | ||
| 285 | { | ||
| 286 | int i = 0; | ||
| 287 | char opt; | ||
| 288 | const char *login; | ||
| 289 | const char *gecos; | ||
| 290 | const char *home = NULL; | ||
| 291 | const char *shell; | ||
| 292 | |||
| 293 | struct passwd pw; | ||
| 294 | |||
| 295 | /* init */ | ||
| 296 | if (argc < 2) { | ||
| 297 | show_usage(); | ||
| 298 | } | ||
| 299 | gecos = default_gecos; | ||
| 300 | shell = default_shell; | ||
| 301 | |||
| 302 | /* get args */ | ||
| 303 | while ((opt = getopt (argc, argv, "h:g:s:")) != -1) | ||
| 304 | switch (opt) { | ||
| 305 | case 'h': | ||
| 306 | home = argv[++i]; | ||
| 307 | break; | ||
| 308 | case 'g': | ||
| 309 | gecos = argv[++i]; | ||
| 310 | break; | ||
| 311 | case 's': | ||
| 312 | shell = argv[++i]; | ||
| 313 | break; | ||
| 314 | default: | ||
| 315 | show_usage (); | ||
| 316 | break; | ||
| 317 | } | ||
| 318 | |||
| 319 | /* got root? */ | ||
| 320 | if (i_am_not_root()) { | ||
| 321 | error_msg_and_die( "Only root may add a user or group to the system."); | ||
| 322 | } | ||
| 323 | |||
| 324 | /* get login */ | ||
| 325 | if (optind >= argc) { | ||
| 326 | error_msg_and_die( "no user specified"); | ||
| 327 | } | ||
| 328 | login = argv[optind]; | ||
| 329 | |||
| 330 | /* create string for $HOME if not specified already */ | ||
| 331 | if (!home) { | ||
| 332 | home = concat_path_file(default_home_prefix, login); | ||
| 333 | } | ||
| 334 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 335 | /* is /etc/shadow in use? */ | ||
| 336 | shadow_enabled = (0 == access(SHADOW_FILE, F_OK)); | ||
| 337 | #endif | ||
| 338 | |||
| 339 | /* create a passwd struct */ | ||
| 340 | pw.pw_name = (char *)login; | ||
| 341 | pw.pw_passwd = (char *)default_passwd; | ||
| 342 | pw.pw_uid = 0; | ||
| 343 | pw.pw_gid = 0; | ||
| 344 | pw.pw_gecos = (char *)gecos; | ||
| 345 | pw.pw_dir = (char *)home; | ||
| 346 | pw.pw_shell = (char *)shell; | ||
| 347 | |||
| 348 | /* grand finale */ | ||
| 349 | return adduser(PASSWD_FILE, &pw); | ||
| 350 | } | ||
| 351 | |||
| 352 | /* $Id: adduser.c,v 1.1 2002/06/04 20:45:05 sandman Exp $ */ | ||
diff --git a/loginutils/config.in b/loginutils/config.in new file mode 100644 index 000000000..265d45ab4 --- /dev/null +++ b/loginutils/config.in | |||
| @@ -0,0 +1,25 @@ | |||
| 1 | # | ||
| 2 | # For a description of the syntax of this configuration file, | ||
| 3 | # see scripts/kbuild/config-language.txt. | ||
| 4 | # | ||
| 5 | |||
| 6 | mainmenu_option next_comment | ||
| 7 | comment 'Login/Password Management Utilities' | ||
| 8 | |||
| 9 | |||
| 10 | bool 'addgroup' CONFIG_ADDGROUP | ||
| 11 | bool 'adduser' CONFIG_ADDUSER | ||
| 12 | bool 'deluser' CONFIG_DELUSER | ||
| 13 | bool 'delgroup' CONFIG_DELUSER | ||
| 14 | bool 'getty' CONFIG_GETTY | ||
| 15 | bool 'login' CONFIG_LOGIN | ||
| 16 | if [ "$CONFIG_LOGIN" = "y" ]; then | ||
| 17 | bool ' Support for /etc/securetty' CONFIG_FEATURE_SECURETTY | ||
| 18 | fi | ||
| 19 | bool 'su' CONFIG_SU | ||
| 20 | if [ "$CONFIG_ADDUSER" = "y" -o "$CONFIG_DELUSER" = "y" -o "$CONFIG_LOGIN" = "y" -o "$CONFIG_SU" = "y" ]; then | ||
| 21 | bool 'Support for shadow passwords' CONFIG_FEATURE_SHADOWPASSWDS | ||
| 22 | fi | ||
| 23 | |||
| 24 | endmenu | ||
| 25 | |||
diff --git a/loginutils/deluser.c b/loginutils/deluser.c new file mode 100644 index 000000000..481a716e7 --- /dev/null +++ b/loginutils/deluser.c | |||
| @@ -0,0 +1,183 @@ | |||
| 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 | #define PASSWD_FILE "/etc/passwd" | ||
| 32 | #define GROUP_FILE "/etc/group" | ||
| 33 | #define SHADOW_FILE "/etc/shadow" | ||
| 34 | #define GSHADOW_FILE "/etc/gshadow" | ||
| 35 | |||
| 36 | |||
| 37 | /* where to start and stop deletion */ | ||
| 38 | typedef struct { | ||
| 39 | size_t start; | ||
| 40 | size_t stop; | ||
| 41 | } Bounds; | ||
| 42 | |||
| 43 | /* An interesting side-effect of boundary()'s | ||
| 44 | * implementation is that the first user (typically root) | ||
| 45 | * cannot be removed. Let's call it a feature. */ | ||
| 46 | static inline Bounds boundary(const char *buffer, const char *login) | ||
| 47 | { | ||
| 48 | char needle[256]; | ||
| 49 | char *start; | ||
| 50 | char *stop; | ||
| 51 | Bounds b; | ||
| 52 | |||
| 53 | snprintf(needle, 256, "\n%s:", login); | ||
| 54 | needle[255] = 0; | ||
| 55 | start = strstr(buffer, needle); | ||
| 56 | if (!start) { | ||
| 57 | b.start = 0; | ||
| 58 | b.stop = 0; | ||
| 59 | return b; | ||
| 60 | } | ||
| 61 | start++; | ||
| 62 | |||
| 63 | stop = index(start, '\n'); /* index is a BSD-ism */ | ||
| 64 | b.start = start - buffer; | ||
| 65 | b.stop = stop - buffer; | ||
| 66 | return b; | ||
| 67 | } | ||
| 68 | |||
| 69 | /* grep -v ^login (except it only deletes the first match) */ | ||
| 70 | /* ...in fact, I think I'm going to simplify this later */ | ||
| 71 | static int del_line_matching(const char *login, const char *filename) | ||
| 72 | { | ||
| 73 | char *buffer; | ||
| 74 | FILE *passwd; | ||
| 75 | size_t len; | ||
| 76 | Bounds b; | ||
| 77 | struct stat statbuf; | ||
| 78 | |||
| 79 | /* load into buffer */ | ||
| 80 | passwd = fopen(filename, "r"); | ||
| 81 | if (!passwd) { | ||
| 82 | return 1; | ||
| 83 | } | ||
| 84 | stat(filename, &statbuf); | ||
| 85 | len = statbuf.st_size; | ||
| 86 | buffer = (char *) malloc(len * sizeof(char)); | ||
| 87 | |||
| 88 | if (!buffer) { | ||
| 89 | fclose(passwd); | ||
| 90 | return 1; | ||
| 91 | } | ||
| 92 | fread(buffer, len, sizeof(char), passwd); | ||
| 93 | |||
| 94 | fclose(passwd); | ||
| 95 | |||
| 96 | /* find the user to remove */ | ||
| 97 | b = boundary(buffer, login); | ||
| 98 | if (b.stop == 0) { | ||
| 99 | free(buffer); | ||
| 100 | return 1; | ||
| 101 | } | ||
| 102 | |||
| 103 | /* write the file w/o the user */ | ||
| 104 | passwd = fopen(filename, "w"); | ||
| 105 | if (!passwd) { | ||
| 106 | return 1; | ||
| 107 | } | ||
| 108 | fwrite(buffer, (b.start - 1), sizeof(char), passwd); | ||
| 109 | fwrite(&buffer[b.stop], (len - b.stop), sizeof(char), passwd); | ||
| 110 | |||
| 111 | fclose(passwd); | ||
| 112 | |||
| 113 | return 0; | ||
| 114 | } | ||
| 115 | |||
| 116 | /* ________________________________________________________________________ */ | ||
| 117 | int delgroup_main(int argc, char **argv) | ||
| 118 | { | ||
| 119 | /* int successful; */ | ||
| 120 | int failure; | ||
| 121 | |||
| 122 | if (argc != 2) { | ||
| 123 | show_usage(); | ||
| 124 | } else { | ||
| 125 | |||
| 126 | failure = del_line_matching(argv[1], GROUP_FILE); | ||
| 127 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 128 | if (access(GSHADOW_FILE, W_OK) == 0) { | ||
| 129 | /* EDR the |= works if the error is not 0, so he had it wrong */ | ||
| 130 | failure |= del_line_matching(argv[1], GSHADOW_FILE); | ||
| 131 | } | ||
| 132 | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ | ||
| 133 | /* if (!successful) { */ | ||
| 134 | if (failure) { | ||
| 135 | error_msg_and_die("%s: Group could not be removed\n", argv[1]); | ||
| 136 | } | ||
| 137 | |||
| 138 | } | ||
| 139 | return (EXIT_SUCCESS); | ||
| 140 | } | ||
| 141 | |||
| 142 | /* ________________________________________________________________________ */ | ||
| 143 | int deluser_main(int argc, char **argv) | ||
| 144 | { | ||
| 145 | /* int successful; */ | ||
| 146 | int failure; | ||
| 147 | |||
| 148 | if (argc != 2) { | ||
| 149 | show_usage(); | ||
| 150 | } else { | ||
| 151 | |||
| 152 | failure = del_line_matching(argv[1], PASSWD_FILE); | ||
| 153 | /* if (!successful) { */ | ||
| 154 | if (failure) { | ||
| 155 | error_msg_and_die("%s: User could not be removed from %s\n", | ||
| 156 | argv[1], PASSWD_FILE); | ||
| 157 | } | ||
| 158 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 159 | failure = del_line_matching(argv[1], SHADOW_FILE); | ||
| 160 | /* if (!successful) { */ | ||
| 161 | if (failure) { | ||
| 162 | error_msg_and_die("%s: User could not be removed from %s\n", | ||
| 163 | argv[1], SHADOW_FILE); | ||
| 164 | } | ||
| 165 | failure = del_line_matching(argv[1], GSHADOW_FILE); | ||
| 166 | /* if (!successful) { */ | ||
| 167 | if (failure) { | ||
| 168 | error_msg_and_die("%s: User could not be removed from %s\n", | ||
| 169 | argv[1], GSHADOW_FILE); | ||
| 170 | } | ||
| 171 | #endif /* CONFIG_FEATURE_SHADOWPASSWDS */ | ||
| 172 | failure = del_line_matching(argv[1], GROUP_FILE); | ||
| 173 | /* if (!successful) { */ | ||
| 174 | if (failure) { | ||
| 175 | error_msg_and_die("%s: User could not be removed from %s\n", | ||
| 176 | argv[1], GROUP_FILE); | ||
| 177 | } | ||
| 178 | |||
| 179 | } | ||
| 180 | return (EXIT_SUCCESS); | ||
| 181 | } | ||
| 182 | |||
| 183 | /* $Id: deluser.c,v 1.1 2002/06/04 20:45:05 sandman Exp $ */ | ||
diff --git a/loginutils/getty.c b/loginutils/getty.c new file mode 100644 index 000000000..2144c95ff --- /dev/null +++ b/loginutils/getty.c | |||
| @@ -0,0 +1,1157 @@ | |||
| 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 | #ifdef linux | ||
| 36 | #include <sys/param.h> | ||
| 37 | #define USE_SYSLOG | ||
| 38 | #endif | ||
| 39 | |||
| 40 | extern void updwtmp(const char *filename, const struct utmp *ut); | ||
| 41 | |||
| 42 | /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */ | ||
| 43 | #ifdef USE_SYSLOG | ||
| 44 | #include <syslog.h> | ||
| 45 | #endif | ||
| 46 | |||
| 47 | |||
| 48 | /* | ||
| 49 | * Some heuristics to find out what environment we are in: if it is not | ||
| 50 | * System V, assume it is SunOS 4. | ||
| 51 | */ | ||
| 52 | |||
| 53 | #ifdef LOGIN_PROCESS /* defined in System V utmp.h */ | ||
| 54 | #define SYSV_STYLE /* select System V style getty */ | ||
| 55 | #endif | ||
| 56 | |||
| 57 | /* | ||
| 58 | * Things you may want to modify. | ||
| 59 | * | ||
| 60 | * If ISSUE is not defined, agetty will never display the contents of the | ||
| 61 | * /etc/issue file. You will not want to spit out large "issue" files at the | ||
| 62 | * wrong baud rate. Relevant for System V only. | ||
| 63 | * | ||
| 64 | * You may disagree with the default line-editing etc. characters defined | ||
| 65 | * below. Note, however, that DEL cannot be used for interrupt generation | ||
| 66 | * and for line editing at the same time. | ||
| 67 | */ | ||
| 68 | |||
| 69 | #ifdef SYSV_STYLE | ||
| 70 | #define ISSUE "/etc/issue" /* displayed before the login prompt */ | ||
| 71 | #include <sys/utsname.h> | ||
| 72 | #include <time.h> | ||
| 73 | #endif | ||
| 74 | |||
| 75 | #define LOGIN " login: " /* login prompt */ | ||
| 76 | |||
| 77 | /* Some shorthands for control characters. */ | ||
| 78 | |||
| 79 | #define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */ | ||
| 80 | #define CR CTL('M') /* carriage return */ | ||
| 81 | #define NL CTL('J') /* line feed */ | ||
| 82 | #define BS CTL('H') /* back space */ | ||
| 83 | #define DEL CTL('?') /* delete */ | ||
| 84 | |||
| 85 | /* Defaults for line-editing etc. characters; you may want to change this. */ | ||
| 86 | |||
| 87 | #define DEF_ERASE DEL /* default erase character */ | ||
| 88 | #define DEF_INTR CTL('C') /* default interrupt character */ | ||
| 89 | #define DEF_QUIT CTL('\\') /* default quit char */ | ||
| 90 | #define DEF_KILL CTL('U') /* default kill char */ | ||
| 91 | #define DEF_EOF CTL('D') /* default EOF char */ | ||
| 92 | #define DEF_EOL 0 | ||
| 93 | #define DEF_SWITCH 0 /* default switch char */ | ||
| 94 | |||
| 95 | /* | ||
| 96 | * SunOS 4.1.1 termio is broken. We must use the termios stuff instead, | ||
| 97 | * because the termio -> termios translation does not clear the termios | ||
| 98 | * CIBAUD bits. Therefore, the tty driver would sometimes report that input | ||
| 99 | * baud rate != output baud rate. I did not notice that problem with SunOS | ||
| 100 | * 4.1. We will use termios where available, and termio otherwise. | ||
| 101 | */ | ||
| 102 | |||
| 103 | /* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set | ||
| 104 | properly, but all is well if we use termios?! */ | ||
| 105 | |||
| 106 | #ifdef TCGETS | ||
| 107 | #undef TCGETA | ||
| 108 | #undef TCSETA | ||
| 109 | #undef TCSETAW | ||
| 110 | #define termio termios | ||
| 111 | #define TCGETA TCGETS | ||
| 112 | #define TCSETA TCSETS | ||
| 113 | #define TCSETAW TCSETSW | ||
| 114 | #endif | ||
| 115 | |||
| 116 | /* | ||
| 117 | * This program tries to not use the standard-i/o library. This keeps the | ||
| 118 | * executable small on systems that do not have shared libraries (System V | ||
| 119 | * Release <3). | ||
| 120 | */ | ||
| 121 | #ifndef BUFSIZ | ||
| 122 | #define BUFSIZ 1024 | ||
| 123 | #endif | ||
| 124 | |||
| 125 | /* | ||
| 126 | * When multiple baud rates are specified on the command line, the first one | ||
| 127 | * we will try is the first one specified. | ||
| 128 | */ | ||
| 129 | |||
| 130 | #define FIRST_SPEED 0 | ||
| 131 | |||
| 132 | /* Storage for command-line options. */ | ||
| 133 | |||
| 134 | #define MAX_SPEED 10 /* max. nr. of baud rates */ | ||
| 135 | |||
| 136 | struct options { | ||
| 137 | int flags; /* toggle switches, see below */ | ||
| 138 | int timeout; /* time-out period */ | ||
| 139 | char *login; /* login program */ | ||
| 140 | char *tty; /* name of tty */ | ||
| 141 | char *initstring; /* modem init string */ | ||
| 142 | char *issue; /* alternative issue file */ | ||
| 143 | int numspeed; /* number of baud rates to try */ | ||
| 144 | int speeds[MAX_SPEED]; /* baud rates to be tried */ | ||
| 145 | }; | ||
| 146 | |||
| 147 | #define F_PARSE (1<<0) /* process modem status messages */ | ||
| 148 | #define F_ISSUE (1<<1) /* display /etc/issue */ | ||
| 149 | #define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ | ||
| 150 | #define F_LOCAL (1<<3) /* force local */ | ||
| 151 | #define F_INITSTRING (1<<4) /* initstring is set */ | ||
| 152 | #define F_WAITCRLF (1<<5) /* wait for CR or LF */ | ||
| 153 | #define F_CUSTISSUE (1<<6) /* give alternative issue file */ | ||
| 154 | #define F_NOPROMPT (1<<7) /* don't ask for login name! */ | ||
| 155 | |||
| 156 | /* Storage for things detected while the login name was read. */ | ||
| 157 | |||
| 158 | struct chardata { | ||
| 159 | int erase; /* erase character */ | ||
| 160 | int kill; /* kill character */ | ||
| 161 | int eol; /* end-of-line character */ | ||
| 162 | int parity; /* what parity did we see */ | ||
| 163 | int capslock; /* upper case without lower case */ | ||
| 164 | }; | ||
| 165 | |||
| 166 | /* Initial values for the above. */ | ||
| 167 | |||
| 168 | struct chardata init_chardata = { | ||
| 169 | DEF_ERASE, /* default erase character */ | ||
| 170 | DEF_KILL, /* default kill character */ | ||
| 171 | 13, /* default eol char */ | ||
| 172 | 0, /* space parity */ | ||
| 173 | 0, /* no capslock */ | ||
| 174 | }; | ||
| 175 | |||
| 176 | struct Speedtab { | ||
| 177 | long speed; | ||
| 178 | int code; | ||
| 179 | }; | ||
| 180 | |||
| 181 | static struct Speedtab speedtab[] = { | ||
| 182 | {50, B50}, | ||
| 183 | {75, B75}, | ||
| 184 | {110, B110}, | ||
| 185 | {134, B134}, | ||
| 186 | {150, B150}, | ||
| 187 | {200, B200}, | ||
| 188 | {300, B300}, | ||
| 189 | {600, B600}, | ||
| 190 | {1200, B1200}, | ||
| 191 | {1800, B1800}, | ||
| 192 | {2400, B2400}, | ||
| 193 | {4800, B4800}, | ||
| 194 | {9600, B9600}, | ||
| 195 | #ifdef B19200 | ||
| 196 | {19200, B19200}, | ||
| 197 | #endif | ||
| 198 | #ifdef B38400 | ||
| 199 | {38400, B38400}, | ||
| 200 | #endif | ||
| 201 | #ifdef EXTA | ||
| 202 | {19200, EXTA}, | ||
| 203 | #endif | ||
| 204 | #ifdef EXTB | ||
| 205 | {38400, EXTB}, | ||
| 206 | #endif | ||
| 207 | #ifdef B57600 | ||
| 208 | {57600, B57600}, | ||
| 209 | #endif | ||
| 210 | #ifdef B115200 | ||
| 211 | {115200, B115200}, | ||
| 212 | #endif | ||
| 213 | #ifdef B230400 | ||
| 214 | {230400, B230400}, | ||
| 215 | #endif | ||
| 216 | {0, 0}, | ||
| 217 | }; | ||
| 218 | |||
| 219 | static void parse_args(int argc, char **argv, struct options *op); | ||
| 220 | static void parse_speeds(struct options *op, char *arg); | ||
| 221 | static void update_utmp(char *line); | ||
| 222 | static void open_tty(char *tty, struct termio *tp, int local); | ||
| 223 | static void termio_init(struct termio *tp, int speed, struct options *op); | ||
| 224 | static void auto_baud(struct termio *tp); | ||
| 225 | static void do_prompt(struct options *op, struct termio *tp); | ||
| 226 | static void next_speed(struct termio *tp, struct options *op); | ||
| 227 | static char *get_logname(struct options *op, struct chardata *cp, | ||
| 228 | |||
| 229 | struct termio *tp); | ||
| 230 | static void termio_final(struct options *op, struct termio *tp, | ||
| 231 | |||
| 232 | struct chardata *cp); | ||
| 233 | static int caps_lock(const char *s); | ||
| 234 | static int bcode(const char *s); | ||
| 235 | static void error(const char *fmt, ...); | ||
| 236 | |||
| 237 | /* The following is used for understandable diagnostics. */ | ||
| 238 | |||
| 239 | /* Fake hostname for ut_host specified on command line. */ | ||
| 240 | static char *fakehost = NULL; | ||
| 241 | |||
| 242 | /* ... */ | ||
| 243 | #ifdef DEBUGGING | ||
| 244 | #define debug(s) fprintf(dbf,s); fflush(dbf) | ||
| 245 | #define DEBUGTERM "/dev/ttyp0" | ||
| 246 | FILE *dbf; | ||
| 247 | #else | ||
| 248 | #define debug(s) /* nothing */ | ||
| 249 | #endif | ||
| 250 | |||
| 251 | int getty_main(int argc, char **argv) | ||
| 252 | { | ||
| 253 | char *logname = NULL; /* login name, given to /bin/login */ | ||
| 254 | struct chardata chardata; /* set by get_logname() */ | ||
| 255 | struct termio termio; /* terminal mode bits */ | ||
| 256 | static struct options options = { | ||
| 257 | F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ | ||
| 258 | 0, /* no timeout */ | ||
| 259 | _PATH_LOGIN, /* default login program */ | ||
| 260 | "tty1", /* default tty line */ | ||
| 261 | "", /* modem init string */ | ||
| 262 | ISSUE, /* default issue file */ | ||
| 263 | 0, /* no baud rates known yet */ | ||
| 264 | }; | ||
| 265 | |||
| 266 | #ifdef DEBUGGING | ||
| 267 | dbf = xfopen(DEBUGTERM, "w"); | ||
| 268 | |||
| 269 | { | ||
| 270 | int i; | ||
| 271 | |||
| 272 | for (i = 1; i < argc; i++) { | ||
| 273 | debug(argv[i]); | ||
| 274 | debug("\n"); | ||
| 275 | } | ||
| 276 | } | ||
| 277 | #endif | ||
| 278 | |||
| 279 | /* Parse command-line arguments. */ | ||
| 280 | |||
| 281 | parse_args(argc, argv, &options); | ||
| 282 | |||
| 283 | #ifdef __linux__ | ||
| 284 | setsid(); | ||
| 285 | #endif | ||
| 286 | |||
| 287 | /* Update the utmp file. */ | ||
| 288 | |||
| 289 | #ifdef SYSV_STYLE | ||
| 290 | update_utmp(options.tty); | ||
| 291 | #endif | ||
| 292 | |||
| 293 | debug("calling open_tty\n"); | ||
| 294 | /* Open the tty as standard { input, output, error }. */ | ||
| 295 | open_tty(options.tty, &termio, options.flags & F_LOCAL); | ||
| 296 | |||
| 297 | #ifdef __linux__ | ||
| 298 | { | ||
| 299 | int iv; | ||
| 300 | |||
| 301 | iv = getpid(); | ||
| 302 | if (ioctl(0, TIOCSPGRP, &iv) < 0) | ||
| 303 | perror_msg("ioctl() TIOCSPGRP call failed"); | ||
| 304 | } | ||
| 305 | #endif | ||
| 306 | /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */ | ||
| 307 | debug("calling termio_init\n"); | ||
| 308 | termio_init(&termio, options.speeds[FIRST_SPEED], &options); | ||
| 309 | |||
| 310 | /* write the modem init string and DON'T flush the buffers */ | ||
| 311 | if (options.flags & F_INITSTRING) { | ||
| 312 | debug("writing init string\n"); | ||
| 313 | write(1, options.initstring, strlen(options.initstring)); | ||
| 314 | } | ||
| 315 | |||
| 316 | if (!(options.flags & F_LOCAL)) { | ||
| 317 | /* go to blocking write mode unless -L is specified */ | ||
| 318 | fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK); | ||
| 319 | } | ||
| 320 | |||
| 321 | /* Optionally detect the baud rate from the modem status message. */ | ||
| 322 | debug("before autobaud\n"); | ||
| 323 | if (options.flags & F_PARSE) | ||
| 324 | auto_baud(&termio); | ||
| 325 | |||
| 326 | /* Set the optional timer. */ | ||
| 327 | if (options.timeout) | ||
| 328 | (void) alarm((unsigned) options.timeout); | ||
| 329 | |||
| 330 | /* optionally wait for CR or LF before writing /etc/issue */ | ||
| 331 | if (options.flags & F_WAITCRLF) { | ||
| 332 | char ch; | ||
| 333 | |||
| 334 | debug("waiting for cr-lf\n"); | ||
| 335 | while (read(0, &ch, 1) == 1) { | ||
| 336 | ch &= 0x7f; /* strip "parity bit" */ | ||
| 337 | #ifdef DEBUGGING | ||
| 338 | fprintf(dbf, "read %c\n", ch); | ||
| 339 | #endif | ||
| 340 | if (ch == '\n' || ch == '\r') | ||
| 341 | break; | ||
| 342 | } | ||
| 343 | } | ||
| 344 | |||
| 345 | chardata = init_chardata; | ||
| 346 | if (!(options.flags & F_NOPROMPT)) { | ||
| 347 | /* Read the login name. */ | ||
| 348 | debug("reading login name\n"); | ||
| 349 | /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */ | ||
| 350 | while ((logname = get_logname(&options, &chardata, &termio)) == | ||
| 351 | NULL) next_speed(&termio, &options); | ||
| 352 | } | ||
| 353 | |||
| 354 | /* Disable timer. */ | ||
| 355 | |||
| 356 | if (options.timeout) | ||
| 357 | (void) alarm(0); | ||
| 358 | |||
| 359 | /* Finalize the termio settings. */ | ||
| 360 | |||
| 361 | termio_final(&options, &termio, &chardata); | ||
| 362 | |||
| 363 | /* Now the newline character should be properly written. */ | ||
| 364 | |||
| 365 | (void) write(1, "\n", 1); | ||
| 366 | |||
| 367 | /* Let the login program take care of password validation. */ | ||
| 368 | |||
| 369 | (void) execl(options.login, options.login, "--", logname, (char *) 0); | ||
| 370 | error("%s: can't exec %s: %m", options.tty, options.login); | ||
| 371 | return (0); /* quiet GCC */ | ||
| 372 | } | ||
| 373 | |||
| 374 | /* parse-args - parse command-line arguments */ | ||
| 375 | |||
| 376 | static void parse_args(int argc, char **argv, struct options *op) | ||
| 377 | { | ||
| 378 | extern char *optarg; /* getopt */ | ||
| 379 | extern int optind; /* getopt */ | ||
| 380 | int c; | ||
| 381 | |||
| 382 | while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) { | ||
| 383 | switch (c) { | ||
| 384 | case 'I': | ||
| 385 | if (!(op->initstring = strdup(optarg))) { | ||
| 386 | error("can't malloc initstring"); | ||
| 387 | break; | ||
| 388 | } | ||
| 389 | { | ||
| 390 | char ch, *p, *q; | ||
| 391 | int i; | ||
| 392 | |||
| 393 | /* copy optarg into op->initstring decoding \ddd | ||
| 394 | octal codes into chars */ | ||
| 395 | q = op->initstring; | ||
| 396 | p = optarg; | ||
| 397 | while (*p) { | ||
| 398 | if (*p == '\\') { /* know \\ means \ */ | ||
| 399 | p++; | ||
| 400 | if (*p == '\\') { | ||
| 401 | ch = '\\'; | ||
| 402 | p++; | ||
| 403 | } else { /* handle \000 - \177 */ | ||
| 404 | ch = 0; | ||
| 405 | for (i = 1; i <= 3; i++) { | ||
| 406 | if (*p >= '0' && *p <= '7') { | ||
| 407 | ch <<= 3; | ||
| 408 | ch += *p - '0'; | ||
| 409 | p++; | ||
| 410 | } else | ||
| 411 | break; | ||
| 412 | } | ||
| 413 | } | ||
| 414 | *q++ = ch; | ||
| 415 | } else { | ||
| 416 | *q++ = *p++; | ||
| 417 | } | ||
| 418 | } | ||
| 419 | *q = '\0'; | ||
| 420 | } | ||
| 421 | op->flags |= F_INITSTRING; | ||
| 422 | break; | ||
| 423 | |||
| 424 | case 'L': /* force local */ | ||
| 425 | op->flags |= F_LOCAL; | ||
| 426 | break; | ||
| 427 | case 'H': /* fake login host */ | ||
| 428 | fakehost = optarg; | ||
| 429 | break; | ||
| 430 | case 'f': /* custom issue file */ | ||
| 431 | op->flags |= F_CUSTISSUE; | ||
| 432 | op->issue = optarg; | ||
| 433 | break; | ||
| 434 | case 'h': /* enable h/w flow control */ | ||
| 435 | op->flags |= F_RTSCTS; | ||
| 436 | break; | ||
| 437 | case 'i': /* do not show /etc/issue */ | ||
| 438 | op->flags &= ~F_ISSUE; | ||
| 439 | break; | ||
| 440 | case 'l': | ||
| 441 | op->login = optarg; /* non-default login program */ | ||
| 442 | break; | ||
| 443 | case 'm': /* parse modem status message */ | ||
| 444 | op->flags |= F_PARSE; | ||
| 445 | break; | ||
| 446 | case 'n': | ||
| 447 | op->flags |= F_NOPROMPT; | ||
| 448 | break; | ||
| 449 | case 't': /* time out */ | ||
| 450 | if ((op->timeout = atoi(optarg)) <= 0) | ||
| 451 | error("bad timeout value: %s", optarg); | ||
| 452 | break; | ||
| 453 | case 'w': | ||
| 454 | op->flags |= F_WAITCRLF; | ||
| 455 | break; | ||
| 456 | default: | ||
| 457 | show_usage(); | ||
| 458 | } | ||
| 459 | } | ||
| 460 | debug("after getopt loop\n"); | ||
| 461 | if (argc < optind + 2) /* check parameter count */ | ||
| 462 | show_usage(); | ||
| 463 | |||
| 464 | /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ | ||
| 465 | if ('0' <= argv[optind][0] && argv[optind][0] <= '9') { | ||
| 466 | /* a number first, assume it's a speed (BSD style) */ | ||
| 467 | parse_speeds(op, argv[optind++]); /* baud rate(s) */ | ||
| 468 | op->tty = argv[optind]; /* tty name */ | ||
| 469 | } else { | ||
| 470 | op->tty = argv[optind++]; /* tty name */ | ||
| 471 | parse_speeds(op, argv[optind]); /* baud rate(s) */ | ||
| 472 | } | ||
| 473 | |||
| 474 | optind++; | ||
| 475 | if (argc > optind && argv[optind]) | ||
| 476 | setenv("TERM", argv[optind], 1); | ||
| 477 | |||
| 478 | debug("exiting parseargs\n"); | ||
| 479 | } | ||
| 480 | |||
| 481 | /* parse_speeds - parse alternate baud rates */ | ||
| 482 | |||
| 483 | static void parse_speeds(struct options *op, char *arg) | ||
| 484 | { | ||
| 485 | char *cp; | ||
| 486 | |||
| 487 | debug("entered parse_speeds\n"); | ||
| 488 | for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { | ||
| 489 | if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) | ||
| 490 | error("bad speed: %s", cp); | ||
| 491 | if (op->numspeed > MAX_SPEED) | ||
| 492 | error("too many alternate speeds"); | ||
| 493 | } | ||
| 494 | debug("exiting parsespeeds\n"); | ||
| 495 | } | ||
| 496 | |||
| 497 | #ifdef SYSV_STYLE | ||
| 498 | |||
| 499 | /* update_utmp - update our utmp entry */ | ||
| 500 | static void update_utmp(char *line) | ||
| 501 | { | ||
| 502 | struct utmp ut; | ||
| 503 | struct utmp *utp; | ||
| 504 | time_t t; | ||
| 505 | int mypid = getpid(); | ||
| 506 | #if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)) | ||
| 507 | struct flock lock; | ||
| 508 | #endif | ||
| 509 | |||
| 510 | /* | ||
| 511 | * The utmp file holds miscellaneous information about things started by | ||
| 512 | * /sbin/init and other system-related events. Our purpose is to update | ||
| 513 | * the utmp entry for the current process, in particular the process type | ||
| 514 | * and the tty line we are listening to. Return successfully only if the | ||
| 515 | * utmp file can be opened for update, and if we are able to find our | ||
| 516 | * entry in the utmp file. | ||
| 517 | */ | ||
| 518 | utmpname(_PATH_UTMP); | ||
| 519 | setutent(); | ||
| 520 | while ((utp = getutent()) | ||
| 521 | && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */ | ||
| 522 | ; | ||
| 523 | |||
| 524 | if (utp) { | ||
| 525 | memcpy(&ut, utp, sizeof(ut)); | ||
| 526 | } else { | ||
| 527 | /* some inits don't initialize utmp... */ | ||
| 528 | memset(&ut, 0, sizeof(ut)); | ||
| 529 | strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); | ||
| 530 | } | ||
| 531 | /*endutent(); */ | ||
| 532 | |||
| 533 | strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user)); | ||
| 534 | strncpy(ut.ut_line, line, sizeof(ut.ut_line)); | ||
| 535 | if (fakehost) | ||
| 536 | strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); | ||
| 537 | time(&t); | ||
| 538 | ut.ut_time = t; | ||
| 539 | ut.ut_type = LOGIN_PROCESS; | ||
| 540 | ut.ut_pid = mypid; | ||
| 541 | |||
| 542 | pututline(&ut); | ||
| 543 | endutent(); | ||
| 544 | |||
| 545 | { | ||
| 546 | updwtmp(_PATH_WTMP, &ut); | ||
| 547 | } | ||
| 548 | } | ||
| 549 | |||
| 550 | #endif | ||
| 551 | |||
| 552 | /* open_tty - set up tty as standard { input, output, error } */ | ||
| 553 | static void open_tty(char *tty, struct termio *tp, int local) | ||
| 554 | { | ||
| 555 | /* Get rid of the present standard { output, error} if any. */ | ||
| 556 | |||
| 557 | (void) close(1); | ||
| 558 | (void) close(2); | ||
| 559 | errno = 0; /* ignore above errors */ | ||
| 560 | |||
| 561 | /* Set up new standard input, unless we are given an already opened port. */ | ||
| 562 | |||
| 563 | if (strcmp(tty, "-")) { | ||
| 564 | struct stat st; | ||
| 565 | |||
| 566 | /* Sanity checks... */ | ||
| 567 | |||
| 568 | if (chdir("/dev")) | ||
| 569 | error("/dev: chdir() failed: %m"); | ||
| 570 | if (stat(tty, &st) < 0) | ||
| 571 | error("/dev/%s: %m", tty); | ||
| 572 | if ((st.st_mode & S_IFMT) != S_IFCHR) | ||
| 573 | error("/dev/%s: not a character device", tty); | ||
| 574 | |||
| 575 | /* Open the tty as standard input. */ | ||
| 576 | |||
| 577 | (void) close(0); | ||
| 578 | errno = 0; /* ignore close(2) errors */ | ||
| 579 | |||
| 580 | debug("open(2)\n"); | ||
| 581 | if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0) | ||
| 582 | error("/dev/%s: cannot open as standard input: %m", tty); | ||
| 583 | |||
| 584 | } else { | ||
| 585 | |||
| 586 | /* | ||
| 587 | * Standard input should already be connected to an open port. Make | ||
| 588 | * sure it is open for read/write. | ||
| 589 | */ | ||
| 590 | |||
| 591 | if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR) | ||
| 592 | error("%s: not open for read/write", tty); | ||
| 593 | } | ||
| 594 | |||
| 595 | /* Set up standard output and standard error file descriptors. */ | ||
| 596 | debug("duping\n"); | ||
| 597 | if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */ | ||
| 598 | error("%s: dup problem: %m", tty); /* we have a problem */ | ||
| 599 | |||
| 600 | /* | ||
| 601 | * The following ioctl will fail if stdin is not a tty, but also when | ||
| 602 | * there is noise on the modem control lines. In the latter case, the | ||
| 603 | * common course of action is (1) fix your cables (2) give the modem more | ||
| 604 | * time to properly reset after hanging up. SunOS users can achieve (2) | ||
| 605 | * by patching the SunOS kernel variable "zsadtrlow" to a larger value; | ||
| 606 | * 5 seconds seems to be a good value. | ||
| 607 | */ | ||
| 608 | |||
| 609 | if (ioctl(0, TCGETA, tp) < 0) | ||
| 610 | error("%s: ioctl: %m", tty); | ||
| 611 | |||
| 612 | /* | ||
| 613 | * It seems to be a terminal. Set proper protections and ownership. Mode | ||
| 614 | * 0622 is suitable for SYSV <4 because /bin/login does not change | ||
| 615 | * protections. SunOS 4 login will change the protections to 0620 (write | ||
| 616 | * access for group tty) after the login has succeeded. | ||
| 617 | */ | ||
| 618 | |||
| 619 | #ifdef DEBIAN | ||
| 620 | { | ||
| 621 | /* tty to root.dialout 660 */ | ||
| 622 | struct group *gr; | ||
| 623 | int id; | ||
| 624 | |||
| 625 | id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0; | ||
| 626 | chown(tty, 0, id); | ||
| 627 | chmod(tty, 0660); | ||
| 628 | |||
| 629 | /* vcs,vcsa to root.sys 600 */ | ||
| 630 | if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) { | ||
| 631 | char *vcs, *vcsa; | ||
| 632 | |||
| 633 | if (!(vcs = strdup(tty))) | ||
| 634 | error("Can't malloc for vcs"); | ||
| 635 | if (!(vcsa = malloc(strlen(tty) + 2))) | ||
| 636 | error("Can't malloc for vcsa"); | ||
| 637 | strcpy(vcs, "vcs"); | ||
| 638 | strcpy(vcs + 3, tty + 3); | ||
| 639 | strcpy(vcsa, "vcsa"); | ||
| 640 | strcpy(vcsa + 4, tty + 3); | ||
| 641 | |||
| 642 | id = (gr = getgrnam("sys")) ? gr->gr_gid : 0; | ||
| 643 | chown(vcs, 0, id); | ||
| 644 | chmod(vcs, 0600); | ||
| 645 | chown(vcsa, 0, id); | ||
| 646 | chmod(vcs, 0600); | ||
| 647 | |||
| 648 | free(vcs); | ||
| 649 | free(vcsa); | ||
| 650 | } | ||
| 651 | } | ||
| 652 | #else | ||
| 653 | (void) chown(tty, 0, 0); /* root, sys */ | ||
| 654 | (void) chmod(tty, 0622); /* crw--w--w- */ | ||
| 655 | errno = 0; /* ignore above errors */ | ||
| 656 | #endif | ||
| 657 | } | ||
| 658 | |||
| 659 | /* termio_init - initialize termio settings */ | ||
| 660 | |||
| 661 | static void termio_init(struct termio *tp, int speed, struct options *op) | ||
| 662 | { | ||
| 663 | |||
| 664 | /* | ||
| 665 | * Initial termio settings: 8-bit characters, raw-mode, blocking i/o. | ||
| 666 | * Special characters are set after we have read the login name; all | ||
| 667 | * reads will be done in raw mode anyway. Errors will be dealt with | ||
| 668 | * lateron. | ||
| 669 | */ | ||
| 670 | #ifdef __linux__ | ||
| 671 | /* flush input and output queues, important for modems! */ | ||
| 672 | (void) ioctl(0, TCFLSH, TCIOFLUSH); | ||
| 673 | #endif | ||
| 674 | |||
| 675 | tp->c_cflag = CS8 | HUPCL | CREAD | speed; | ||
| 676 | if (op->flags & F_LOCAL) { | ||
| 677 | tp->c_cflag |= CLOCAL; | ||
| 678 | } | ||
| 679 | |||
| 680 | tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0; | ||
| 681 | tp->c_cc[VMIN] = 1; | ||
| 682 | tp->c_cc[VTIME] = 0; | ||
| 683 | |||
| 684 | /* Optionally enable hardware flow control */ | ||
| 685 | |||
| 686 | #ifdef CRTSCTS | ||
| 687 | if (op->flags & F_RTSCTS) | ||
| 688 | tp->c_cflag |= CRTSCTS; | ||
| 689 | #endif | ||
| 690 | |||
| 691 | (void) ioctl(0, TCSETA, tp); | ||
| 692 | |||
| 693 | /* go to blocking input even in local mode */ | ||
| 694 | fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK); | ||
| 695 | |||
| 696 | debug("term_io 2\n"); | ||
| 697 | } | ||
| 698 | |||
| 699 | /* auto_baud - extract baud rate from modem status message */ | ||
| 700 | static void auto_baud(struct termio *tp) | ||
| 701 | { | ||
| 702 | int speed; | ||
| 703 | int vmin; | ||
| 704 | unsigned iflag; | ||
| 705 | char buf[BUFSIZ]; | ||
| 706 | char *bp; | ||
| 707 | int nread; | ||
| 708 | |||
| 709 | /* | ||
| 710 | * This works only if the modem produces its status code AFTER raising | ||
| 711 | * the DCD line, and if the computer is fast enough to set the proper | ||
| 712 | * baud rate before the message has gone by. We expect a message of the | ||
| 713 | * following format: | ||
| 714 | * | ||
| 715 | * <junk><number><junk> | ||
| 716 | * | ||
| 717 | * The number is interpreted as the baud rate of the incoming call. If the | ||
| 718 | * modem does not tell us the baud rate within one second, we will keep | ||
| 719 | * using the current baud rate. It is advisable to enable BREAK | ||
| 720 | * processing (comma-separated list of baud rates) if the processing of | ||
| 721 | * modem status messages is enabled. | ||
| 722 | */ | ||
| 723 | |||
| 724 | /* | ||
| 725 | * Use 7-bit characters, don't block if input queue is empty. Errors will | ||
| 726 | * be dealt with lateron. | ||
| 727 | */ | ||
| 728 | |||
| 729 | iflag = tp->c_iflag; | ||
| 730 | tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ | ||
| 731 | vmin = tp->c_cc[VMIN]; | ||
| 732 | tp->c_cc[VMIN] = 0; /* don't block if queue empty */ | ||
| 733 | (void) ioctl(0, TCSETA, tp); | ||
| 734 | |||
| 735 | /* | ||
| 736 | * Wait for a while, then read everything the modem has said so far and | ||
| 737 | * try to extract the speed of the dial-in call. | ||
| 738 | */ | ||
| 739 | |||
| 740 | (void) sleep(1); | ||
| 741 | if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) { | ||
| 742 | buf[nread] = '\0'; | ||
| 743 | for (bp = buf; bp < buf + nread; bp++) { | ||
| 744 | if (isascii(*bp) && isdigit(*bp)) { | ||
| 745 | if ((speed = bcode(bp))) { | ||
| 746 | tp->c_cflag &= ~CBAUD; | ||
| 747 | tp->c_cflag |= speed; | ||
| 748 | } | ||
| 749 | break; | ||
| 750 | } | ||
| 751 | } | ||
| 752 | } | ||
| 753 | /* Restore terminal settings. Errors will be dealt with lateron. */ | ||
| 754 | |||
| 755 | tp->c_iflag = iflag; | ||
| 756 | tp->c_cc[VMIN] = vmin; | ||
| 757 | (void) ioctl(0, TCSETA, tp); | ||
| 758 | } | ||
| 759 | |||
| 760 | /* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ | ||
| 761 | static void do_prompt(struct options *op, struct termio *tp) | ||
| 762 | { | ||
| 763 | #ifdef ISSUE | ||
| 764 | FILE *fd; | ||
| 765 | int oflag; | ||
| 766 | int c; | ||
| 767 | struct utsname uts; | ||
| 768 | |||
| 769 | (void) uname(&uts); | ||
| 770 | #endif | ||
| 771 | |||
| 772 | (void) write(1, "\r\n", 2); /* start a new line */ | ||
| 773 | #ifdef ISSUE /* optional: show /etc/issue */ | ||
| 774 | if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) { | ||
| 775 | oflag = tp->c_oflag; /* save current setting */ | ||
| 776 | tp->c_oflag |= (ONLCR | OPOST); /* map NL in output to CR-NL */ | ||
| 777 | (void) ioctl(0, TCSETAW, tp); | ||
| 778 | |||
| 779 | |||
| 780 | while ((c = getc(fd)) != EOF) { | ||
| 781 | if (c == '\\') { | ||
| 782 | c = getc(fd); | ||
| 783 | |||
| 784 | switch (c) { | ||
| 785 | case 's': | ||
| 786 | (void) printf("%s", uts.sysname); | ||
| 787 | break; | ||
| 788 | |||
| 789 | case 'n': | ||
| 790 | (void) printf("%s", uts.nodename); | ||
| 791 | break; | ||
| 792 | |||
| 793 | case 'r': | ||
| 794 | (void) printf("%s", uts.release); | ||
| 795 | break; | ||
| 796 | |||
| 797 | case 'v': | ||
| 798 | (void) printf("%s", uts.version); | ||
| 799 | break; | ||
| 800 | |||
| 801 | case 'm': | ||
| 802 | (void) printf("%s", uts.machine); | ||
| 803 | break; | ||
| 804 | |||
| 805 | case 'o': | ||
| 806 | { | ||
| 807 | char domainname[256]; | ||
| 808 | |||
| 809 | getdomainname(domainname, sizeof(domainname)); | ||
| 810 | domainname[sizeof(domainname) - 1] = '\0'; | ||
| 811 | printf("%s", domainname); | ||
| 812 | } | ||
| 813 | break; | ||
| 814 | |||
| 815 | case 'd': | ||
| 816 | case 't': | ||
| 817 | { | ||
| 818 | char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", | ||
| 819 | "Fri", "Sat" | ||
| 820 | }; | ||
| 821 | char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", | ||
| 822 | "Jun", "Jul", "Aug", "Sep", "Oct", | ||
| 823 | "Nov", "Dec" | ||
| 824 | }; | ||
| 825 | time_t now; | ||
| 826 | struct tm *tm; | ||
| 827 | |||
| 828 | (void) time(&now); | ||
| 829 | tm = localtime(&now); | ||
| 830 | |||
| 831 | if (c == 'd') | ||
| 832 | (void) printf("%s %s %d %d", | ||
| 833 | weekday[tm->tm_wday], | ||
| 834 | month[tm->tm_mon], tm->tm_mday, | ||
| 835 | tm->tm_year < | ||
| 836 | 70 ? tm->tm_year + | ||
| 837 | 2000 : tm->tm_year + 1900); | ||
| 838 | else | ||
| 839 | (void) printf("%02d:%02d:%02d", tm->tm_hour, | ||
| 840 | tm->tm_min, tm->tm_sec); | ||
| 841 | |||
| 842 | break; | ||
| 843 | } | ||
| 844 | |||
| 845 | case 'l': | ||
| 846 | (void) printf("%s", op->tty); | ||
| 847 | break; | ||
| 848 | |||
| 849 | case 'b': | ||
| 850 | { | ||
| 851 | int i; | ||
| 852 | |||
| 853 | for (i = 0; speedtab[i].speed; i++) { | ||
| 854 | if (speedtab[i].code == (tp->c_cflag & CBAUD)) { | ||
| 855 | printf("%ld", speedtab[i].speed); | ||
| 856 | break; | ||
| 857 | } | ||
| 858 | } | ||
| 859 | break; | ||
| 860 | } | ||
| 861 | case 'u': | ||
| 862 | case 'U': | ||
| 863 | { | ||
| 864 | int users = 0; | ||
| 865 | struct utmp *ut; | ||
| 866 | |||
| 867 | setutent(); | ||
| 868 | while ((ut = getutent())) | ||
| 869 | if (ut->ut_type == USER_PROCESS) | ||
| 870 | users++; | ||
| 871 | endutent(); | ||
| 872 | printf("%d ", users); | ||
| 873 | if (c == 'U') | ||
| 874 | printf((users == 1) ? "user" : "users"); | ||
| 875 | break; | ||
| 876 | } | ||
| 877 | default: | ||
| 878 | (void) putchar(c); | ||
| 879 | } | ||
| 880 | } else | ||
| 881 | (void) putchar(c); | ||
| 882 | } | ||
| 883 | fflush(stdout); | ||
| 884 | |||
| 885 | tp->c_oflag = oflag; /* restore settings */ | ||
| 886 | (void) ioctl(0, TCSETAW, tp); /* wait till output is gone */ | ||
| 887 | (void) fclose(fd); | ||
| 888 | } | ||
| 889 | #endif | ||
| 890 | #ifdef __linux__ | ||
| 891 | { | ||
| 892 | char hn[MAXHOSTNAMELEN + 1]; | ||
| 893 | |||
| 894 | (void) gethostname(hn, MAXHOSTNAMELEN); | ||
| 895 | write(1, hn, strlen(hn)); | ||
| 896 | } | ||
| 897 | #endif | ||
| 898 | (void) write(1, LOGIN, sizeof(LOGIN) - 1); /* always show login prompt */ | ||
| 899 | } | ||
| 900 | |||
| 901 | /* next_speed - select next baud rate */ | ||
| 902 | static void next_speed(struct termio *tp, struct options *op) | ||
| 903 | { | ||
| 904 | static int baud_index = FIRST_SPEED; /* current speed index */ | ||
| 905 | |||
| 906 | baud_index = (baud_index + 1) % op->numspeed; | ||
| 907 | tp->c_cflag &= ~CBAUD; | ||
| 908 | tp->c_cflag |= op->speeds[baud_index]; | ||
| 909 | (void) ioctl(0, TCSETA, tp); | ||
| 910 | } | ||
| 911 | |||
| 912 | /* get_logname - get user name, establish parity, speed, erase, kill, eol */ | ||
| 913 | /* return NULL on failure, logname on success */ | ||
| 914 | static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp) | ||
| 915 | { | ||
| 916 | static char logname[BUFSIZ]; | ||
| 917 | char *bp; | ||
| 918 | char c; /* input character, full eight bits */ | ||
| 919 | char ascval; /* low 7 bits of input character */ | ||
| 920 | int bits; /* # of "1" bits per character */ | ||
| 921 | int mask; /* mask with 1 bit up */ | ||
| 922 | static char *erase[] = { /* backspace-space-backspace */ | ||
| 923 | "\010\040\010", /* space parity */ | ||
| 924 | "\010\040\010", /* odd parity */ | ||
| 925 | "\210\240\210", /* even parity */ | ||
| 926 | "\210\240\210", /* no parity */ | ||
| 927 | }; | ||
| 928 | |||
| 929 | /* Initialize kill, erase, parity etc. (also after switching speeds). */ | ||
| 930 | |||
| 931 | *cp = init_chardata; | ||
| 932 | |||
| 933 | /* Flush pending input (esp. after parsing or switching the baud rate). */ | ||
| 934 | |||
| 935 | (void) sleep(1); | ||
| 936 | (void) ioctl(0, TCFLSH, TCIFLUSH); | ||
| 937 | |||
| 938 | /* Prompt for and read a login name. */ | ||
| 939 | |||
| 940 | for (*logname = 0; *logname == 0; /* void */ ) { | ||
| 941 | |||
| 942 | /* Write issue file and prompt, with "parity" bit == 0. */ | ||
| 943 | |||
| 944 | do_prompt(op, tp); | ||
| 945 | |||
| 946 | /* Read name, watch for break, parity, erase, kill, end-of-line. */ | ||
| 947 | |||
| 948 | for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) { | ||
| 949 | |||
| 950 | /* Do not report trivial EINTR/EIO errors. */ | ||
| 951 | |||
| 952 | if (read(0, &c, 1) < 1) { | ||
| 953 | if (errno == EINTR || errno == EIO) | ||
| 954 | exit(0); | ||
| 955 | error("%s: read: %m", op->tty); | ||
| 956 | } | ||
| 957 | /* Do BREAK handling elsewhere. */ | ||
| 958 | |||
| 959 | if ((c == 0) && op->numspeed > 1) | ||
| 960 | /* return (0); */ | ||
| 961 | return NULL; | ||
| 962 | |||
| 963 | /* Do parity bit handling. */ | ||
| 964 | |||
| 965 | if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */ | ||
| 966 | for (bits = 1, mask = 1; mask & 0177; mask <<= 1) | ||
| 967 | if (mask & ascval) | ||
| 968 | bits++; /* count "1" bits */ | ||
| 969 | cp->parity |= ((bits & 1) ? 1 : 2); | ||
| 970 | } | ||
| 971 | /* Do erase, kill and end-of-line processing. */ | ||
| 972 | |||
| 973 | switch (ascval) { | ||
| 974 | case CR: | ||
| 975 | case NL: | ||
| 976 | *bp = 0; /* terminate logname */ | ||
| 977 | cp->eol = ascval; /* set end-of-line char */ | ||
| 978 | break; | ||
| 979 | case BS: | ||
| 980 | case DEL: | ||
| 981 | case '#': | ||
| 982 | cp->erase = ascval; /* set erase character */ | ||
| 983 | if (bp > logname) { | ||
| 984 | (void) write(1, erase[cp->parity], 3); | ||
| 985 | bp--; | ||
| 986 | } | ||
| 987 | break; | ||
| 988 | case CTL('U'): | ||
| 989 | case '@': | ||
| 990 | cp->kill = ascval; /* set kill character */ | ||
| 991 | while (bp > logname) { | ||
| 992 | (void) write(1, erase[cp->parity], 3); | ||
| 993 | bp--; | ||
| 994 | } | ||
| 995 | break; | ||
| 996 | case CTL('D'): | ||
| 997 | exit(0); | ||
| 998 | default: | ||
| 999 | if (!isascii(ascval) || !isprint(ascval)) { | ||
| 1000 | /* ignore garbage characters */ ; | ||
| 1001 | } else if (bp - logname >= sizeof(logname) - 1) { | ||
| 1002 | error("%s: input overrun", op->tty); | ||
| 1003 | } else { | ||
| 1004 | (void) write(1, &c, 1); /* echo the character */ | ||
| 1005 | *bp++ = ascval; /* and store it */ | ||
| 1006 | } | ||
| 1007 | break; | ||
| 1008 | } | ||
| 1009 | } | ||
| 1010 | } | ||
| 1011 | /* Handle names with upper case and no lower case. */ | ||
| 1012 | |||
| 1013 | if ((cp->capslock = caps_lock(logname))) { | ||
| 1014 | for (bp = logname; *bp; bp++) | ||
| 1015 | if (isupper(*bp)) | ||
| 1016 | *bp = tolower(*bp); /* map name to lower case */ | ||
| 1017 | } | ||
| 1018 | return (logname); | ||
| 1019 | } | ||
| 1020 | |||
| 1021 | /* termio_final - set the final tty mode bits */ | ||
| 1022 | static void termio_final(struct options *op, struct termio *tp, struct chardata *cp) | ||
| 1023 | { | ||
| 1024 | /* General terminal-independent stuff. */ | ||
| 1025 | |||
| 1026 | tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ | ||
| 1027 | tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; | ||
| 1028 | /* no longer| ECHOCTL | ECHOPRT */ | ||
| 1029 | tp->c_oflag |= OPOST; | ||
| 1030 | /* tp->c_cflag = 0; */ | ||
| 1031 | tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */ | ||
| 1032 | tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */ | ||
| 1033 | tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */ | ||
| 1034 | tp->c_cc[VEOL] = DEF_EOL; | ||
| 1035 | #ifdef __linux__ | ||
| 1036 | tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ | ||
| 1037 | #else | ||
| 1038 | tp->c_cc[VSWTCH] = DEF_SWITCH; /* default switch character */ | ||
| 1039 | #endif | ||
| 1040 | |||
| 1041 | /* Account for special characters seen in input. */ | ||
| 1042 | |||
| 1043 | if (cp->eol == CR) { | ||
| 1044 | tp->c_iflag |= ICRNL; /* map CR in input to NL */ | ||
| 1045 | tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ | ||
| 1046 | } | ||
| 1047 | tp->c_cc[VERASE] = cp->erase; /* set erase character */ | ||
| 1048 | tp->c_cc[VKILL] = cp->kill; /* set kill character */ | ||
| 1049 | |||
| 1050 | /* Account for the presence or absence of parity bits in input. */ | ||
| 1051 | |||
| 1052 | switch (cp->parity) { | ||
| 1053 | case 0: /* space (always 0) parity */ | ||
| 1054 | break; | ||
| 1055 | case 1: /* odd parity */ | ||
| 1056 | tp->c_cflag |= PARODD; | ||
| 1057 | /* FALLTHROUGH */ | ||
| 1058 | case 2: /* even parity */ | ||
| 1059 | tp->c_cflag |= PARENB; | ||
| 1060 | tp->c_iflag |= INPCK | ISTRIP; | ||
| 1061 | /* FALLTHROUGH */ | ||
| 1062 | case (1 | 2): /* no parity bit */ | ||
| 1063 | tp->c_cflag &= ~CSIZE; | ||
| 1064 | tp->c_cflag |= CS7; | ||
| 1065 | break; | ||
| 1066 | } | ||
| 1067 | /* Account for upper case without lower case. */ | ||
| 1068 | |||
| 1069 | if (cp->capslock) { | ||
| 1070 | tp->c_iflag |= IUCLC; | ||
| 1071 | tp->c_lflag |= XCASE; | ||
| 1072 | tp->c_oflag |= OLCUC; | ||
| 1073 | } | ||
| 1074 | /* Optionally enable hardware flow control */ | ||
| 1075 | |||
| 1076 | #ifdef CRTSCTS | ||
| 1077 | if (op->flags & F_RTSCTS) | ||
| 1078 | tp->c_cflag |= CRTSCTS; | ||
| 1079 | #endif | ||
| 1080 | |||
| 1081 | /* Finally, make the new settings effective */ | ||
| 1082 | |||
| 1083 | if (ioctl(0, TCSETA, tp) < 0) | ||
| 1084 | error("%s: ioctl: TCSETA: %m", op->tty); | ||
| 1085 | } | ||
| 1086 | |||
| 1087 | /* caps_lock - string contains upper case without lower case */ | ||
| 1088 | /* returns 1 if true, 0 if false */ | ||
| 1089 | static int caps_lock(const char *s) | ||
| 1090 | { | ||
| 1091 | int capslock; | ||
| 1092 | |||
| 1093 | for (capslock = 0; *s; s++) { | ||
| 1094 | if (islower(*s)) | ||
| 1095 | return (0); | ||
| 1096 | if (capslock == 0) | ||
| 1097 | capslock = isupper(*s); | ||
| 1098 | } | ||
| 1099 | return (capslock); | ||
| 1100 | } | ||
| 1101 | |||
| 1102 | /* bcode - convert speed string to speed code; return 0 on failure */ | ||
| 1103 | static int bcode(const char *s) | ||
| 1104 | { | ||
| 1105 | struct Speedtab *sp; | ||
| 1106 | long speed = atol(s); | ||
| 1107 | |||
| 1108 | for (sp = speedtab; sp->speed; sp++) | ||
| 1109 | if (sp->speed == speed) | ||
| 1110 | return (sp->code); | ||
| 1111 | return (0); | ||
| 1112 | } | ||
| 1113 | |||
| 1114 | /* error - report errors to console or syslog; only understands %s and %m */ | ||
| 1115 | |||
| 1116 | #define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2) | ||
| 1117 | |||
| 1118 | /* | ||
| 1119 | * output error messages | ||
| 1120 | */ | ||
| 1121 | static void error(const char *fmt, ...) | ||
| 1122 | { | ||
| 1123 | va_list va_alist; | ||
| 1124 | char buf[256], *bp; | ||
| 1125 | |||
| 1126 | #ifndef USE_SYSLOG | ||
| 1127 | int fd; | ||
| 1128 | #endif | ||
| 1129 | |||
| 1130 | #ifdef USE_SYSLOG | ||
| 1131 | buf[0] = '\0'; | ||
| 1132 | bp = buf; | ||
| 1133 | #else | ||
| 1134 | strncpy(buf, applet_name, 256); | ||
| 1135 | strncat(buf, ": ", 256); | ||
| 1136 | buf[255] = 0; | ||
| 1137 | bp = buf + strlen(buf); | ||
| 1138 | #endif | ||
| 1139 | |||
| 1140 | va_start(va_alist, fmt); | ||
| 1141 | vsnprintf(bp, 256 - strlen(buf), fmt, va_alist); | ||
| 1142 | buf[255] = 0; | ||
| 1143 | va_end(va_alist); | ||
| 1144 | |||
| 1145 | #ifdef USE_SYSLOG | ||
| 1146 | syslog_msg(LOG_AUTH, LOG_ERR, buf); | ||
| 1147 | #else | ||
| 1148 | strncat(bp, "\r\n", 256 - strlen(buf)); | ||
| 1149 | buf[255] = 0; | ||
| 1150 | if ((fd = open("/dev/console", 1)) >= 0) { | ||
| 1151 | write(fd, buf, strlen(buf)); | ||
| 1152 | close(fd); | ||
| 1153 | } | ||
| 1154 | #endif | ||
| 1155 | (void) sleep((unsigned) 10); /* be kind to init(8) */ | ||
| 1156 | exit(1); | ||
| 1157 | } | ||
diff --git a/loginutils/login.c b/loginutils/login.c new file mode 100644 index 000000000..4d93ece49 --- /dev/null +++ b/loginutils/login.c | |||
| @@ -0,0 +1,459 @@ | |||
| 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 | #include "busybox.h" | ||
| 18 | |||
| 19 | #include "pwd.h" | ||
| 20 | #include "grp.h" | ||
| 21 | |||
| 22 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 23 | #include "shadow.h" | ||
| 24 | #endif | ||
| 25 | |||
| 26 | #include "tinylogin.h" | ||
| 27 | |||
| 28 | // import from utmp.c | ||
| 29 | static void checkutmp(int picky); | ||
| 30 | static void setutmp(const char *name, const char *line); | ||
| 31 | |||
| 32 | // import from encrypt.c | ||
| 33 | extern char *pw_encrypt(const char *clear, const char *salt); | ||
| 34 | |||
| 35 | |||
| 36 | // login defines | ||
| 37 | #define DEFAULT_USER "UNKNOWN" | ||
| 38 | #define DEFAULT_PWD "!" | ||
| 39 | #define DEFAULT_SHELL "/bin/sh" | ||
| 40 | #define TIMEOUT 60 | ||
| 41 | #define FAIL_DELAY 3 | ||
| 42 | #define MOTD_FILE "/etc/motd" | ||
| 43 | #define NOLOGIN_FILE "/etc/nologin" | ||
| 44 | #define SECURETTY_FILE "/etc/securetty" | ||
| 45 | |||
| 46 | /* Stuff global to this file */ | ||
| 47 | struct utmp utent; | ||
| 48 | |||
| 49 | |||
| 50 | static int check_nologin ( int amroot ); | ||
| 51 | |||
| 52 | #if defined CONFIG_FEATURE_SECURETTY | ||
| 53 | static int check_tty ( const char *tty ); | ||
| 54 | |||
| 55 | #else | ||
| 56 | static inline int check_tty ( const char *tty ) { return 1; } | ||
| 57 | |||
| 58 | #endif | ||
| 59 | |||
| 60 | static int is_my_tty ( const char *tty ); | ||
| 61 | static const char *login_prompt ( void ); | ||
| 62 | static void motd ( void ); | ||
| 63 | static void set_env(int argc, char *const *argv); | ||
| 64 | |||
| 65 | |||
| 66 | static void alarm_handler ( int sig ) | ||
| 67 | { | ||
| 68 | error_msg ( "\nLogin timed out after %d seconds.\n", TIMEOUT ); | ||
| 69 | exit ( EXIT_SUCCESS ); | ||
| 70 | } | ||
| 71 | |||
| 72 | |||
| 73 | extern int login_main(int argc, char **argv) | ||
| 74 | { | ||
| 75 | char tty[BUFSIZ]; | ||
| 76 | char full_tty[200]; | ||
| 77 | char fromhost[512]; | ||
| 78 | char *tmp; | ||
| 79 | int amroot; | ||
| 80 | int flag; | ||
| 81 | int failed; | ||
| 82 | int count=0; | ||
| 83 | struct passwd *pw, pw_copy; | ||
| 84 | |||
| 85 | int opt_preserve = 0; | ||
| 86 | int opt_fflag = 0; | ||
| 87 | char *opt_host = 0; | ||
| 88 | const char *username = 0; | ||
| 89 | int alarmstarted = 0; | ||
| 90 | |||
| 91 | amroot = ( getuid ( ) == 0 ); | ||
| 92 | signal ( SIGALRM, alarm_handler ); | ||
| 93 | |||
| 94 | if (( argc > 1 ) && ( TIMEOUT > 0 )) { | ||
| 95 | alarm ( TIMEOUT ); | ||
| 96 | alarmstarted = 1; | ||
| 97 | } | ||
| 98 | |||
| 99 | while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) { | ||
| 100 | switch ( flag ) { | ||
| 101 | case 'p': | ||
| 102 | printf ( "PRESERVE\n" ); | ||
| 103 | opt_preserve = 1; | ||
| 104 | break; | ||
| 105 | case 'f': | ||
| 106 | /* | ||
| 107 | * username must be a seperate token | ||
| 108 | * (-f root, *NOT* -froot). --marekm | ||
| 109 | */ | ||
| 110 | if ( optarg != argv[optind-1] ) | ||
| 111 | show_usage ( ); | ||
| 112 | |||
| 113 | if ( !amroot ) /* Auth bypass only if real UID is zero */ | ||
| 114 | error_msg_and_die ( "login: -f permission denied\n" ); | ||
| 115 | |||
| 116 | printf ( "USERNAME: %s\n", optarg ); | ||
| 117 | |||
| 118 | username = optarg; | ||
| 119 | opt_fflag = 1; | ||
| 120 | break; | ||
| 121 | case 'h': | ||
| 122 | opt_host = optarg; | ||
| 123 | break; | ||
| 124 | default: | ||
| 125 | show_usage ( ); | ||
| 126 | } | ||
| 127 | } | ||
| 128 | |||
| 129 | if ( optind < argc ) // got a username | ||
| 130 | username = argv [optind++]; | ||
| 131 | |||
| 132 | if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 )) | ||
| 133 | return EXIT_FAILURE; /* Must be a terminal */ | ||
| 134 | |||
| 135 | checkutmp ( !amroot ); | ||
| 136 | |||
| 137 | tmp = ttyname ( 0 ); | ||
| 138 | if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 )) | ||
| 139 | safe_strncpy ( tty, tmp + 5, sizeof( tty )); | ||
| 140 | else | ||
| 141 | safe_strncpy ( tty, "UNKNOWN", sizeof( tty )); | ||
| 142 | |||
| 143 | if ( amroot ) | ||
| 144 | memset ( utent.ut_host, 0, sizeof utent.ut_host ); | ||
| 145 | |||
| 146 | if ( opt_host ) { | ||
| 147 | safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host )); | ||
| 148 | |||
| 149 | snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host ); | ||
| 150 | } | ||
| 151 | else | ||
| 152 | snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty ); | ||
| 153 | |||
| 154 | openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH ); | ||
| 155 | |||
| 156 | while ( 1 ) { | ||
| 157 | failed = 0; | ||
| 158 | |||
| 159 | if ( !username || !username[0] ) | ||
| 160 | username = login_prompt ( ); | ||
| 161 | |||
| 162 | if ( !alarmstarted && ( TIMEOUT > 0 )) { | ||
| 163 | alarm ( TIMEOUT ); | ||
| 164 | alarmstarted = 1; | ||
| 165 | } | ||
| 166 | |||
| 167 | if (!( pw = getpwnam ( username ))) { | ||
| 168 | pw_copy. pw_name = DEFAULT_USER; | ||
| 169 | pw_copy. pw_passwd = DEFAULT_PWD; | ||
| 170 | pw_copy. pw_shell = DEFAULT_SHELL; | ||
| 171 | opt_fflag = 0; | ||
| 172 | failed = 1; | ||
| 173 | } else | ||
| 174 | pw_copy = *pw; | ||
| 175 | |||
| 176 | pw = &pw_copy; | ||
| 177 | |||
| 178 | if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' )) | ||
| 179 | failed = 1; | ||
| 180 | |||
| 181 | if ( opt_fflag ) { | ||
| 182 | opt_fflag = 0; | ||
| 183 | goto auth_ok; | ||
| 184 | } | ||
| 185 | |||
| 186 | if (( pw-> pw_uid == 0 ) && ( !check_tty ( tty ))) | ||
| 187 | failed = 1; | ||
| 188 | |||
| 189 | /* Don't check the password if password entry is empty (!) */ | ||
| 190 | if ( !pw-> pw_passwd[0] ) | ||
| 191 | goto auth_ok; | ||
| 192 | |||
| 193 | /* authorization takes place here */ | ||
| 194 | if ( correct_password ( pw )) | ||
| 195 | goto auth_ok; | ||
| 196 | |||
| 197 | syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw-> pw_name, fromhost); | ||
| 198 | failed = 1; | ||
| 199 | |||
| 200 | auth_ok: | ||
| 201 | if ( !failed) | ||
| 202 | break; | ||
| 203 | |||
| 204 | { // delay next try | ||
| 205 | time_t start, now; | ||
| 206 | |||
| 207 | time ( &start ); | ||
| 208 | now = start; | ||
| 209 | while ( difftime ( now, start ) < FAIL_DELAY) { | ||
| 210 | sleep ( FAIL_DELAY ); | ||
| 211 | time ( &now ); | ||
| 212 | } | ||
| 213 | } | ||
| 214 | |||
| 215 | puts("Login incorrect"); | ||
| 216 | if ( ++count == 3 ) | ||
| 217 | return EXIT_FAILURE; | ||
| 218 | } | ||
| 219 | |||
| 220 | alarm ( 0 ); | ||
| 221 | if ( check_nologin ( pw-> pw_uid == 0 )) | ||
| 222 | return EXIT_FAILURE; | ||
| 223 | |||
| 224 | setutmp ( username, tty ); | ||
| 225 | if ( *tty != '/' ) | ||
| 226 | snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty); | ||
| 227 | else | ||
| 228 | safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 ); | ||
| 229 | |||
| 230 | if ( !is_my_tty ( full_tty )) | ||
| 231 | syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty ); | ||
| 232 | |||
| 233 | /* Try these, but don't complain if they fail | ||
| 234 | * (for example when the root fs is read only) */ | ||
| 235 | chown ( full_tty, pw-> pw_uid, pw-> pw_gid ); | ||
| 236 | chmod ( full_tty, 0600 ); | ||
| 237 | |||
| 238 | change_identity ( pw ); | ||
| 239 | setup_environment ( pw-> pw_shell, 1, !opt_preserve, pw ); | ||
| 240 | |||
| 241 | motd ( ); | ||
| 242 | signal ( SIGALRM, SIG_DFL ); /* default alarm signal */ | ||
| 243 | |||
| 244 | if ( pw-> pw_uid == 0 ) | ||
| 245 | syslog ( LOG_INFO, "root login %s\n", fromhost ); | ||
| 246 | |||
| 247 | run_shell ( pw-> pw_shell, 1, 0, 0 ); /* exec the shell finally. */ | ||
| 248 | |||
| 249 | return EXIT_FAILURE; | ||
| 250 | } | ||
| 251 | |||
| 252 | |||
| 253 | |||
| 254 | static const char *login_prompt ( void ) | ||
| 255 | { | ||
| 256 | char buf [1024]; | ||
| 257 | char *sp, *ep; | ||
| 258 | |||
| 259 | gethostname ( buf, sizeof( buf )); | ||
| 260 | printf ( "\nBusyBox on %s login: ", buf ); | ||
| 261 | fflush ( stdout ); | ||
| 262 | |||
| 263 | if ( !fgets ( buf, sizeof( buf ) - 1, stdin )) | ||
| 264 | return 0; | ||
| 265 | |||
| 266 | if ( !strchr ( buf, '\n' )); | ||
| 267 | return 0; | ||
| 268 | |||
| 269 | for ( sp = buf; isspace ( *sp ); sp++ ) { } | ||
| 270 | for ( ep = sp; isgraph ( *ep ); ep++ ) { } | ||
| 271 | |||
| 272 | *ep = 0; | ||
| 273 | return sp; | ||
| 274 | } | ||
| 275 | |||
| 276 | |||
| 277 | static int check_nologin ( int amroot ) | ||
| 278 | { | ||
| 279 | if ( access ( NOLOGIN_FILE, F_OK ) == 0 ) { | ||
| 280 | FILE *fp; | ||
| 281 | int c; | ||
| 282 | |||
| 283 | if (( fp = fopen ( NOLOGIN_FILE, "r" ))) { | ||
| 284 | while (( c = getc ( fp )) != EOF ) | ||
| 285 | putchar (( c == '\n' ) ? '\r' : c ); | ||
| 286 | |||
| 287 | fflush ( stdout ); | ||
| 288 | fclose ( fp ); | ||
| 289 | } else { | ||
| 290 | puts ( "\r\nSystem closed for routine maintenance.\r" ); | ||
| 291 | } | ||
| 292 | if ( !amroot ) | ||
| 293 | return 1; | ||
| 294 | |||
| 295 | puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" ); | ||
| 296 | } | ||
| 297 | return 0; | ||
| 298 | } | ||
| 299 | |||
| 300 | #ifdef CONFIG_FEATURE_SECURETTY | ||
| 301 | |||
| 302 | static int check_tty ( const char *tty ) | ||
| 303 | { | ||
| 304 | FILE *fp; | ||
| 305 | int i; | ||
| 306 | char buf[BUFSIZ]; | ||
| 307 | |||
| 308 | if (( fp = fopen ( SECURETTY_FILE, "r" ))) { | ||
| 309 | while ( fgets ( buf, sizeof( buf ) - 1, fp )) { | ||
| 310 | for ( i = xstrlen( buf ) - 1; i >= 0; --i ) { | ||
| 311 | if ( !isspace ( buf[i] )) | ||
| 312 | break; | ||
| 313 | } | ||
| 314 | buf[++i] = '\0'; | ||
| 315 | if (( buf [0] == '\0' ) || ( buf [0] == '#' )) | ||
| 316 | continue; | ||
| 317 | |||
| 318 | if ( strcmp ( buf, tty ) == 0 ) { | ||
| 319 | fclose ( fp ); | ||
| 320 | return 1; | ||
| 321 | } | ||
| 322 | } | ||
| 323 | fclose(fp); | ||
| 324 | return 0; | ||
| 325 | } | ||
| 326 | else { | ||
| 327 | syslog ( LOG_WARNING, "cannot open securetty file.\n" ); | ||
| 328 | return 1; | ||
| 329 | } | ||
| 330 | } | ||
| 331 | |||
| 332 | #endif | ||
| 333 | |||
| 334 | /* returns 1 if true */ | ||
| 335 | static int is_my_tty ( const char *tty ) | ||
| 336 | { | ||
| 337 | struct stat by_name, by_fd; | ||
| 338 | |||
| 339 | if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd )) | ||
| 340 | return 0; | ||
| 341 | |||
| 342 | if ( by_name. st_rdev != by_fd. st_rdev ) | ||
| 343 | return 0; | ||
| 344 | else | ||
| 345 | return 1; | ||
| 346 | } | ||
| 347 | |||
| 348 | |||
| 349 | static void motd ( ) | ||
| 350 | { | ||
| 351 | FILE *fp; | ||
| 352 | register int c; | ||
| 353 | |||
| 354 | if (( fp = fopen ( MOTD_FILE, "r" ))) { | ||
| 355 | while (( c = getc ( fp )) != EOF ) | ||
| 356 | putchar ( c ); | ||
| 357 | fclose ( fp ); | ||
| 358 | } | ||
| 359 | } | ||
| 360 | |||
| 361 | |||
| 362 | // vv Taken from tinylogin utmp.c vv | ||
| 363 | |||
| 364 | #define _WTMP_FILE "/var/log/wtmp" | ||
| 365 | |||
| 366 | #define NO_UTENT \ | ||
| 367 | "No utmp entry. You must exec \"login\" from the lowest level \"sh\"" | ||
| 368 | #define NO_TTY \ | ||
| 369 | "Unable to determine your tty name." | ||
| 370 | |||
| 371 | /* | ||
| 372 | * checkutmp - see if utmp file is correct for this process | ||
| 373 | * | ||
| 374 | * System V is very picky about the contents of the utmp file | ||
| 375 | * and requires that a slot for the current process exist. | ||
| 376 | * The utmp file is scanned for an entry with the same process | ||
| 377 | * ID. If no entry exists the process exits with a message. | ||
| 378 | * | ||
| 379 | * The "picky" flag is for network and other logins that may | ||
| 380 | * use special flags. It allows the pid checks to be overridden. | ||
| 381 | * This means that getty should never invoke login with any | ||
| 382 | * command line flags. | ||
| 383 | */ | ||
| 384 | |||
| 385 | static void checkutmp(int picky) | ||
| 386 | { | ||
| 387 | char *line; | ||
| 388 | struct utmp *ut; | ||
| 389 | pid_t pid = getpid(); | ||
| 390 | |||
| 391 | setutent(); | ||
| 392 | |||
| 393 | /* First, try to find a valid utmp entry for this process. */ | ||
| 394 | while ((ut = getutent())) | ||
| 395 | if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] && | ||
| 396 | (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS)) | ||
| 397 | break; | ||
| 398 | |||
| 399 | /* If there is one, just use it, otherwise create a new one. */ | ||
| 400 | if (ut) { | ||
| 401 | utent = *ut; | ||
| 402 | } else { | ||
| 403 | if (picky) { | ||
| 404 | puts(NO_UTENT); | ||
| 405 | exit(1); | ||
| 406 | } | ||
| 407 | line = ttyname(0); | ||
| 408 | if (!line) { | ||
| 409 | puts(NO_TTY); | ||
| 410 | exit(1); | ||
| 411 | } | ||
| 412 | if (strncmp(line, "/dev/", 5) == 0) | ||
| 413 | line += 5; | ||
| 414 | memset((void *) &utent, 0, sizeof utent); | ||
| 415 | utent.ut_type = LOGIN_PROCESS; | ||
| 416 | utent.ut_pid = pid; | ||
| 417 | strncpy(utent.ut_line, line, sizeof utent.ut_line); | ||
| 418 | /* XXX - assumes /dev/tty?? */ | ||
| 419 | strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id); | ||
| 420 | strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user); | ||
| 421 | time(&utent.ut_time); | ||
| 422 | } | ||
| 423 | } | ||
| 424 | |||
| 425 | #if __GNU_LIBRARY__ < 5 | ||
| 426 | /* | ||
| 427 | * Some systems already have updwtmp() and possibly updwtmpx(). Others | ||
| 428 | * don't, so we re-implement these functions if necessary. --marekm | ||
| 429 | */ | ||
| 430 | static void updwtmp(const char *filename, const struct utmp *ut) | ||
| 431 | { | ||
| 432 | int fd; | ||
| 433 | |||
| 434 | fd = open(filename, O_APPEND | O_WRONLY, 0); | ||
| 435 | if (fd >= 0) { | ||
| 436 | write(fd, (const char *) ut, sizeof(*ut)); | ||
| 437 | close(fd); | ||
| 438 | } | ||
| 439 | } | ||
| 440 | #endif | ||
| 441 | |||
| 442 | /* | ||
| 443 | * setutmp - put a USER_PROCESS entry in the utmp file | ||
| 444 | * | ||
| 445 | * setutmp changes the type of the current utmp entry to | ||
| 446 | * USER_PROCESS. the wtmp file will be updated as well. | ||
| 447 | */ | ||
| 448 | |||
| 449 | static void setutmp(const char *name, const char *line) | ||
| 450 | { | ||
| 451 | utent.ut_type = USER_PROCESS; | ||
| 452 | strncpy(utent.ut_user, name, sizeof utent.ut_user); | ||
| 453 | time(&utent.ut_time); | ||
| 454 | /* other fields already filled in by checkutmp above */ | ||
| 455 | setutent(); | ||
| 456 | pututline(&utent); | ||
| 457 | endutent(); | ||
| 458 | updwtmp(_WTMP_FILE, &utent); | ||
| 459 | } | ||
diff --git a/loginutils/su.c b/loginutils/su.c new file mode 100644 index 000000000..33e62e837 --- /dev/null +++ b/loginutils/su.c | |||
| @@ -0,0 +1,167 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | |||
| 3 | #include "busybox.h" | ||
| 4 | |||
| 5 | #include <fcntl.h> | ||
| 6 | #include <signal.h> | ||
| 7 | #include <stdio.h> | ||
| 8 | #include <stdlib.h> | ||
| 9 | #include <string.h> | ||
| 10 | #include <syslog.h> | ||
| 11 | #include <termios.h> | ||
| 12 | #include <unistd.h> | ||
| 13 | #include <utmp.h> | ||
| 14 | #include <sys/resource.h> | ||
| 15 | #include <sys/stat.h> | ||
| 16 | #include <sys/time.h> | ||
| 17 | #include <sys/types.h> | ||
| 18 | #include <ctype.h> | ||
| 19 | #include <time.h> | ||
| 20 | |||
| 21 | #include "pwd.h" | ||
| 22 | #include "grp.h" | ||
| 23 | |||
| 24 | #include "tinylogin.h" | ||
| 25 | |||
| 26 | |||
| 27 | |||
| 28 | /* The shell to run if none is given in the user's passwd entry. */ | ||
| 29 | #define DEFAULT_SHELL "/bin/sh" | ||
| 30 | #define DEFAULT_USER "root" | ||
| 31 | |||
| 32 | //#define SYSLOG_SUCCESS | ||
| 33 | #define SYSLOG_FAILURE | ||
| 34 | |||
| 35 | |||
| 36 | #if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE ) | ||
| 37 | /* Log the fact that someone has run su to the user given by PW; | ||
| 38 | if SUCCESSFUL is nonzero, they gave the correct password, etc. */ | ||
| 39 | |||
| 40 | static void log_su ( const struct passwd *pw, int successful ) | ||
| 41 | { | ||
| 42 | const char *old_user, *tty; | ||
| 43 | |||
| 44 | #if !defined( SYSLOG_SUCESS ) | ||
| 45 | if ( successful ) | ||
| 46 | return; | ||
| 47 | #endif | ||
| 48 | #if !defined( SYSLOG_FAILURE ) | ||
| 49 | if ( !successful ) | ||
| 50 | return; | ||
| 51 | #endif | ||
| 52 | |||
| 53 | if ( pw-> pw_uid ) // not to root -> ignored | ||
| 54 | return; | ||
| 55 | |||
| 56 | /* The utmp entry (via getlogin) is probably the best way to identify | ||
| 57 | the user, especially if someone su's from a su-shell. */ | ||
| 58 | old_user = getlogin ( ); | ||
| 59 | if ( !old_user ) { | ||
| 60 | /* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid. */ | ||
| 61 | struct passwd *pwd = getpwuid ( getuid ( )); | ||
| 62 | old_user = ( pwd ? pwd-> pw_name : "" ); | ||
| 63 | } | ||
| 64 | |||
| 65 | tty = ttyname ( 2 ); | ||
| 66 | |||
| 67 | openlog ( "su", 0, LOG_AUTH ); | ||
| 68 | syslog ( LOG_NOTICE, "%s%s on %s", successful ? "" : "FAILED SU ", old_user, tty ? tty : "none" ); | ||
| 69 | } | ||
| 70 | #endif | ||
| 71 | |||
| 72 | |||
| 73 | |||
| 74 | int su_main ( int argc, char **argv ) | ||
| 75 | { | ||
| 76 | int flag; | ||
| 77 | int opt_preserve = 0; | ||
| 78 | int opt_loginshell = 0; | ||
| 79 | char *opt_shell = 0; | ||
| 80 | char *opt_command = 0; | ||
| 81 | char *opt_username = DEFAULT_USER; | ||
| 82 | char **opt_args = 0; | ||
| 83 | struct passwd *pw, pw_copy; | ||
| 84 | |||
| 85 | |||
| 86 | while (( flag = getopt ( argc, argv, "c:lmps:" )) != -1 ) { | ||
| 87 | switch ( flag ) { | ||
| 88 | case 'c': | ||
| 89 | opt_command = optarg; | ||
| 90 | break; | ||
| 91 | case 'm': | ||
| 92 | case 'p': | ||
| 93 | opt_preserve = 1; | ||
| 94 | break; | ||
| 95 | case 's': | ||
| 96 | opt_shell = optarg; | ||
| 97 | break; | ||
| 98 | case 'l': | ||
| 99 | opt_loginshell = 1; | ||
| 100 | break; | ||
| 101 | default: | ||
| 102 | show_usage ( ); | ||
| 103 | break; | ||
| 104 | } | ||
| 105 | } | ||
| 106 | |||
| 107 | if (( optind < argc ) && ( argv [optind][0] == '-' ) && ( argv [optind][1] == 0 )) { | ||
| 108 | opt_loginshell = 1; | ||
| 109 | ++optind; | ||
| 110 | } | ||
| 111 | |||
| 112 | /* get user if specified */ | ||
| 113 | if ( optind < argc ) | ||
| 114 | opt_username = argv [optind++]; | ||
| 115 | |||
| 116 | if ( optind < argc ) | ||
| 117 | opt_args = argv + optind; | ||
| 118 | |||
| 119 | |||
| 120 | pw = getpwnam ( opt_username ); | ||
| 121 | if ( !pw ) | ||
| 122 | error_msg_and_die ( "user %s does not exist", opt_username ); | ||
| 123 | |||
| 124 | /* Make sure pw->pw_shell is non-NULL. It may be NULL when NEW_USER | ||
| 125 | is a username that is retrieved via NIS (YP), but that doesn't have | ||
| 126 | a default shell listed. */ | ||
| 127 | if ( !pw-> pw_shell || !pw->pw_shell [0] ) | ||
| 128 | pw-> pw_shell = (char *) DEFAULT_SHELL; | ||
| 129 | |||
| 130 | /* Make a copy of the password information and point pw at the local | ||
| 131 | copy instead. Otherwise, some systems (e.g. Linux) would clobber | ||
| 132 | the static data through the getlogin call from log_su. */ | ||
| 133 | pw_copy = *pw; | ||
| 134 | pw = &pw_copy; | ||
| 135 | pw-> pw_name = xstrdup ( pw-> pw_name ); | ||
| 136 | pw-> pw_dir = xstrdup ( pw-> pw_dir ); | ||
| 137 | pw-> pw_shell = xstrdup ( pw-> pw_shell ); | ||
| 138 | |||
| 139 | if (( getuid ( ) == 0 ) || correct_password ( pw )) | ||
| 140 | log_su ( pw, 1 ); | ||
| 141 | else { | ||
| 142 | log_su ( pw, 0 ); | ||
| 143 | error_msg_and_die ( "incorrect password" ); | ||
| 144 | } | ||
| 145 | |||
| 146 | if ( !opt_shell && opt_preserve ) | ||
| 147 | opt_shell = getenv ( "SHELL" ); | ||
| 148 | |||
| 149 | if ( opt_shell && getuid ( ) && restricted_shell ( pw-> pw_shell )) | ||
| 150 | { | ||
| 151 | /* The user being su'd to has a nonstandard shell, and so is | ||
| 152 | probably a uucp account or has restricted access. Don't | ||
| 153 | compromise the account by allowing access with a standard | ||
| 154 | shell. */ | ||
| 155 | fputs ( "using restricted shell\n", stderr ); | ||
| 156 | opt_shell = 0; | ||
| 157 | } | ||
| 158 | |||
| 159 | if ( !opt_shell ) | ||
| 160 | opt_shell = xstrdup ( pw-> pw_shell ); | ||
| 161 | |||
| 162 | change_identity ( pw ); | ||
| 163 | setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw ); | ||
| 164 | run_shell ( opt_shell, opt_loginshell, opt_command, opt_args ); | ||
| 165 | |||
| 166 | return EXIT_FAILURE; | ||
| 167 | } | ||
diff --git a/loginutils/tinylogin.c b/loginutils/tinylogin.c new file mode 100644 index 000000000..bd611fd70 --- /dev/null +++ b/loginutils/tinylogin.c | |||
| @@ -0,0 +1,209 @@ | |||
| 1 | /* vi: set sw=4 ts=4: */ | ||
| 2 | /* | ||
| 3 | * Copyright 1989 - 1991, Julianne Frances Haugh <jockgrrl@austin.rr.com> | ||
| 4 | * All rights reserved. | ||
| 5 | * | ||
| 6 | * Redistribution and use in source and binary forms, with or without | ||
| 7 | * modification, are permitted provided that the following conditions | ||
| 8 | * are met: | ||
| 9 | * 1. Redistributions of source code must retain the above copyright | ||
| 10 | * notice, this list of conditions and the following disclaimer. | ||
| 11 | * 2. Redistributions in binary form must reproduce the above copyright | ||
| 12 | * notice, this list of conditions and the following disclaimer in the | ||
| 13 | * documentation and/or other materials provided with the distribution. | ||
| 14 | * 3. Neither the name of Julianne F. Haugh nor the names of its contributors | ||
| 15 | * may be used to endorse or promote products derived from this software | ||
| 16 | * without specific prior written permission. | ||
| 17 | * | ||
| 18 | * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND | ||
| 19 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | ||
| 20 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | ||
| 21 | * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE | ||
| 22 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | ||
| 23 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | ||
| 24 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | ||
| 25 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | ||
| 26 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | ||
| 27 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | ||
| 28 | * SUCH DAMAGE. | ||
| 29 | */ | ||
| 30 | |||
| 31 | #include "busybox.h" | ||
| 32 | |||
| 33 | #include <stdio.h> | ||
| 34 | #include <errno.h> | ||
| 35 | #include <unistd.h> | ||
| 36 | #include <string.h> | ||
| 37 | #include <stdlib.h> | ||
| 38 | #include <syslog.h> | ||
| 39 | #include <ctype.h> | ||
| 40 | |||
| 41 | #include "pwd.h" | ||
| 42 | #include "grp.h" | ||
| 43 | |||
| 44 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 45 | #include "shadow.h" | ||
| 46 | #endif | ||
| 47 | |||
| 48 | #define DEFAULT_LOGIN_PATH "/bin:/usr/bin" | ||
| 49 | #define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin" | ||
| 50 | |||
| 51 | |||
| 52 | static void xsetenv ( const char *key, const char *value ) | ||
| 53 | { | ||
| 54 | if ( setenv ( key, value, 1 )) | ||
| 55 | error_msg_and_die ( "out of memory" ); | ||
| 56 | } | ||
| 57 | |||
| 58 | /* Become the user and group(s) specified by PW. */ | ||
| 59 | |||
| 60 | void change_identity ( const struct passwd *pw ) | ||
| 61 | { | ||
| 62 | if ( initgroups ( pw-> pw_name, pw-> pw_gid ) == -1 ) | ||
| 63 | perror_msg_and_die ( "cannot set groups" ); | ||
| 64 | endgrent ( ); | ||
| 65 | |||
| 66 | if ( setgid ( pw-> pw_gid )) | ||
| 67 | perror_msg_and_die ( "cannot set group id" ); | ||
| 68 | if ( setuid ( pw->pw_uid )) | ||
| 69 | perror_msg_and_die ( "cannot set user id" ); | ||
| 70 | } | ||
| 71 | |||
| 72 | /* Run SHELL, or DEFAULT_SHELL if SHELL is empty. | ||
| 73 | If COMMAND is nonzero, pass it to the shell with the -c option. | ||
| 74 | If ADDITIONAL_ARGS is nonzero, pass it to the shell as more | ||
| 75 | arguments. */ | ||
| 76 | |||
| 77 | void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args ) | ||
| 78 | { | ||
| 79 | const char **args; | ||
| 80 | int argno = 1; | ||
| 81 | int additional_args_cnt = 0; | ||
| 82 | |||
| 83 | for ( args = additional_args; args && *args; args++ ) | ||
| 84 | additional_args_cnt++; | ||
| 85 | |||
| 86 | if ( additional_args ) | ||
| 87 | args = (const char **) xmalloc (sizeof (char *) * ( 4 + additional_args_cnt )); | ||
| 88 | else | ||
| 89 | args = (const char **) xmalloc (sizeof (char *) * 4 ); | ||
| 90 | |||
| 91 | args [0] = get_last_path_component ( xstrdup ( shell )); | ||
| 92 | |||
| 93 | if ( loginshell ) { | ||
| 94 | char *args0 = xmalloc ( xstrlen ( args [0] ) + 2 ); | ||
| 95 | args0 [0] = '-'; | ||
| 96 | strcpy ( args0 + 1, args [0] ); | ||
| 97 | args [0] = args0; | ||
| 98 | } | ||
| 99 | |||
| 100 | if ( command ) { | ||
| 101 | args [argno++] = "-c"; | ||
| 102 | args [argno++] = command; | ||
| 103 | } | ||
| 104 | if ( additional_args ) { | ||
| 105 | for ( ; *additional_args; ++additional_args ) | ||
| 106 | args [argno++] = *additional_args; | ||
| 107 | } | ||
| 108 | args [argno] = 0; | ||
| 109 | execv ( shell, (char **) args ); | ||
| 110 | perror_msg_and_die ( "cannot run %s", shell ); | ||
| 111 | } | ||
| 112 | |||
| 113 | /* Return 1 if SHELL is a restricted shell (one not returned by | ||
| 114 | getusershell), else 0, meaning it is a standard shell. */ | ||
| 115 | |||
| 116 | int restricted_shell ( const char *shell ) | ||
| 117 | { | ||
| 118 | char *line; | ||
| 119 | |||
| 120 | setusershell ( ); | ||
| 121 | while (( line = getusershell ( ))) { | ||
| 122 | if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 )) | ||
| 123 | break; | ||
| 124 | } | ||
| 125 | endusershell ( ); | ||
| 126 | return line ? 0 : 1; | ||
| 127 | } | ||
| 128 | |||
| 129 | /* Update `environ' for the new shell based on PW, with SHELL being | ||
| 130 | the value for the SHELL environment variable. */ | ||
| 131 | |||
| 132 | void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ) | ||
| 133 | { | ||
| 134 | if ( loginshell ) { | ||
| 135 | char *term; | ||
| 136 | |||
| 137 | /* Change the current working directory to be the home directory | ||
| 138 | * of the user. It is a fatal error for this process to be unable | ||
| 139 | * to change to that directory. There is no "default" home | ||
| 140 | * directory. | ||
| 141 | * Some systems default to HOME=/ | ||
| 142 | */ | ||
| 143 | if ( chdir ( pw-> pw_dir )) { | ||
| 144 | if ( chdir ( "/" )) { | ||
| 145 | syslog ( LOG_WARNING, "unable to cd to %s' for user %s'\n", pw-> pw_dir, pw-> pw_name ); | ||
| 146 | error_msg_and_die ( "cannot cd to home directory or /" ); | ||
| 147 | } | ||
| 148 | fputs ( "warning: cannot change to home directory\n", stderr ); | ||
| 149 | } | ||
| 150 | |||
| 151 | /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. | ||
| 152 | Unset all other environment variables. */ | ||
| 153 | term = getenv ("TERM"); | ||
| 154 | clearenv ( ); | ||
| 155 | if ( term ) | ||
| 156 | xsetenv ( "TERM", term ); | ||
| 157 | xsetenv ( "HOME", pw-> pw_dir ); | ||
| 158 | xsetenv ( "SHELL", shell ); | ||
| 159 | xsetenv ( "USER", pw-> pw_name ); | ||
| 160 | xsetenv ( "LOGNAME", pw-> pw_name ); | ||
| 161 | xsetenv ( "PATH", ( pw-> pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH )); | ||
| 162 | } | ||
| 163 | else if ( changeenv ) { | ||
| 164 | /* Set HOME, SHELL, and if not becoming a super-user, | ||
| 165 | USER and LOGNAME. */ | ||
| 166 | xsetenv ( "HOME", pw-> pw_dir ); | ||
| 167 | xsetenv ( "SHELL", shell ); | ||
| 168 | if ( pw-> pw_uid ) { | ||
| 169 | xsetenv ( "USER", pw-> pw_name ); | ||
| 170 | xsetenv ( "LOGNAME", pw-> pw_name ); | ||
| 171 | } | ||
| 172 | } | ||
| 173 | } | ||
| 174 | |||
| 175 | /* Ask the user for a password. | ||
| 176 | Return 1 if the user gives the correct password for entry PW, | ||
| 177 | 0 if not. Return 1 without asking for a password if run by UID 0 | ||
| 178 | or if PW has an empty password. */ | ||
| 179 | |||
| 180 | int correct_password ( const struct passwd *pw ) | ||
| 181 | { | ||
| 182 | char *unencrypted, *encrypted, *correct; | ||
| 183 | |||
| 184 | #ifdef CONFIG_FEATURE_SHADOWPASSWDS | ||
| 185 | if (( strcmp ( pw-> pw_passwd, "x" ) == 0 ) || ( strcmp ( pw-> pw_passwd, "*" ) == 0 )) { | ||
| 186 | struct spwd *sp = getspnam ( pw-> pw_name ); | ||
| 187 | |||
| 188 | if ( !sp ) | ||
| 189 | error_msg_and_die ( "no valid shadow password" ); | ||
| 190 | |||
| 191 | correct = sp-> sp_pwdp; | ||
| 192 | } | ||
| 193 | else | ||
| 194 | #endif | ||
| 195 | correct = pw-> pw_passwd; | ||
| 196 | |||
| 197 | if ( correct == 0 || correct[0] == '\0' ) | ||
| 198 | return 1; | ||
| 199 | |||
| 200 | unencrypted = getpass ( "Password: " ); | ||
| 201 | if ( !unencrypted ) | ||
| 202 | { | ||
| 203 | fputs ( "getpass: cannot open /dev/tty\n", stderr ); | ||
| 204 | return 0; | ||
| 205 | } | ||
| 206 | encrypted = crypt ( unencrypted, correct ); | ||
| 207 | memset ( unencrypted, 0, xstrlen ( unencrypted )); | ||
| 208 | return ( strcmp ( encrypted, correct ) == 0 ) ? 1 : 0; | ||
| 209 | } | ||
diff --git a/loginutils/tinylogin.h b/loginutils/tinylogin.h new file mode 100644 index 000000000..5e56a2c7f --- /dev/null +++ b/loginutils/tinylogin.h | |||
| @@ -0,0 +1,10 @@ | |||
| 1 | #ifndef BB_LOGINUTILS_SHELL_H | ||
| 2 | #define BB_LOGINUTILS_SHELL_H | ||
| 3 | |||
| 4 | extern void change_identity ( const struct passwd *pw ); | ||
| 5 | extern void run_shell ( const char *shell, int loginshell, const char *command, char **additional_args ); | ||
| 6 | extern int restricted_shell ( const char *shell ); | ||
| 7 | extern void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ); | ||
| 8 | extern int correct_password ( const struct passwd *pw ); | ||
| 9 | |||
| 10 | #endif | ||
