From 1fca558799350fb6caff97f55aa6d1237b08fccb Mon Sep 17 00:00:00 2001 From: Robert Griebl Date: Tue, 4 Jun 2002 20:45:46 +0000 Subject: 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] --- Makefile | 4 +- include/usage.h | 16 + libpwdgrp/Makefile | 30 ++ libpwdgrp/Makefile.in | 35 ++ libpwdgrp/__getgrent.c | 204 +++++++++ libpwdgrp/__getpwent.c | 115 +++++ libpwdgrp/fgetgrent.c | 35 ++ libpwdgrp/fgetpwent.c | 35 ++ libpwdgrp/getgrgid.c | 44 ++ libpwdgrp/getgrnam.c | 50 +++ libpwdgrp/getpw.c | 47 ++ libpwdgrp/getpwnam.c | 51 +++ libpwdgrp/getpwuid.c | 44 ++ libpwdgrp/grent.c | 54 +++ libpwdgrp/initgroups.c | 115 +++++ libpwdgrp/putpwent.c | 39 ++ libpwdgrp/pwent.c | 58 +++ libpwdgrp/setgroups.c | 42 ++ libpwdgrp/shadow.c | 302 +++++++++++++ loginutils/Makefile | 30 ++ loginutils/Makefile.in | 45 ++ loginutils/addgroup.c | 168 +++++++ loginutils/adduser.c | 352 +++++++++++++++ loginutils/config.in | 25 ++ loginutils/deluser.c | 183 ++++++++ loginutils/getty.c | 1157 ++++++++++++++++++++++++++++++++++++++++++++++++ loginutils/login.c | 459 +++++++++++++++++++ loginutils/su.c | 167 +++++++ loginutils/tinylogin.c | 209 +++++++++ loginutils/tinylogin.h | 10 + 30 files changed, 4123 insertions(+), 2 deletions(-) create mode 100644 libpwdgrp/Makefile create mode 100644 libpwdgrp/Makefile.in create mode 100644 libpwdgrp/__getgrent.c create mode 100644 libpwdgrp/__getpwent.c create mode 100644 libpwdgrp/fgetgrent.c create mode 100644 libpwdgrp/fgetpwent.c create mode 100644 libpwdgrp/getgrgid.c create mode 100644 libpwdgrp/getgrnam.c create mode 100644 libpwdgrp/getpw.c create mode 100644 libpwdgrp/getpwnam.c create mode 100644 libpwdgrp/getpwuid.c create mode 100644 libpwdgrp/grent.c create mode 100644 libpwdgrp/initgroups.c create mode 100644 libpwdgrp/putpwent.c create mode 100644 libpwdgrp/pwent.c create mode 100644 libpwdgrp/setgroups.c create mode 100644 libpwdgrp/shadow.c create mode 100644 loginutils/Makefile create mode 100644 loginutils/Makefile.in create mode 100644 loginutils/addgroup.c create mode 100644 loginutils/adduser.c create mode 100644 loginutils/config.in create mode 100644 loginutils/deluser.c create mode 100644 loginutils/getty.c create mode 100644 loginutils/login.c create mode 100644 loginutils/su.c create mode 100644 loginutils/tinylogin.c create mode 100644 loginutils/tinylogin.h diff --git a/Makefile b/Makefile index f86ebedf7..dc299cded 100644 --- a/Makefile +++ b/Makefile @@ -22,8 +22,8 @@ include $(TOPDIR).config include $(TOPDIR)Rules.mak SUBDIRS:=applets archival archival/libunarchive console-tools \ editors fileutils findutils init miscutils modutils networking \ - procps pwd_grp pwd_grp/libpwd_grp shell shellutils sysklogd \ - textutils util-linux libbb + procps loginutils shell shellutils sysklogd \ + textutils util-linux libbb libpwdgrp all: do-it-all diff --git a/include/usage.h b/include/usage.h index 823d95296..08ee00d77 100644 --- a/include/usage.h +++ b/include/usage.h @@ -978,6 +978,15 @@ #define logger_example_usage \ "$ logger "hello"\n" +#define login_trivial_usage \ + "[OPTION]... [username] [ENV=VAR ...]" +#define login_full_usage \ + "Begin a new session on the system\n\n" \ + "Options:\n" \ + "\t-f\tDo not authenticate (user already authenticated)\n" \ + "\t-h\tName of the remote host for this login.\n" \ + "\t-p\tPreserve environment." + #define logname_trivial_usage \ "" #define logname_full_usage \ @@ -1570,6 +1579,13 @@ "\n\t-g\t\tprint in stty-readable form" \ "\n\t[SETTING]\tsee manpage" +#define su_trivial_usage \ + "[OPTION]... [-] [username]" +#define su_full_usage \ + "Change user id or become root.\n" \ + "Options:\n" \ + "\t-p\tPreserve environment" + #define swapoff_trivial_usage \ "[OPTION] [DEVICE]" #define swapoff_full_usage \ diff --git a/libpwdgrp/Makefile b/libpwdgrp/Makefile new file mode 100644 index 000000000..2708027ec --- /dev/null +++ b/libpwdgrp/Makefile @@ -0,0 +1,30 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2002 Erik Andersen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +TOPDIR:= ../ +LIBPWDGRP_DIR:=./ +include $(TOPDIR).config +include $(TOPDIR)Rules.mak +include Makefile.in +all: $(libraries-y) +-include $(TOPDIR).depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/libpwdgrp/Makefile.in b/libpwdgrp/Makefile.in new file mode 100644 index 000000000..970457112 --- /dev/null +++ b/libpwdgrp/Makefile.in @@ -0,0 +1,35 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2002 Erik Andersen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +LIBPWDGRP_AR:=libpwdgrp.a +ifndef $(LIBPWDGRP_DIR) +LIBPWDGRP_DIR:=$(TOPDIR)libpwdgrp/ +endif + +LIBPWDGRP-y:= +LIBPWDGRP-$(CONFIG_USE_BB_PWD_GRP) += __getgrent.o __getgrent.o __getpwent.o\ + fgetgrent.o fgetpwent.o getgrgid.o getgrnam.o getpw.o getpwnam.o \ + getpwuid.o grent.o initgroups.o putpwent.o pwent.o setgroups.o +LIBPWDGRP-$(CONFIG_USE_BB_SHADOW) += shadow.o + +libraries-y+=$(LIBPWDGRP_DIR)$(LIBPWDGRP_AR) + +$(LIBPWDGRP_DIR)$(LIBPWDGRP_AR): $(patsubst %,$(LIBPWDGRP_DIR)%, $(LIBPWDGRP-y)) + $(AR) -ro $@ $(patsubst %,$(LIBPWDGRP_DIR)%, $(LIBPWDGRP-y)) + diff --git a/libpwdgrp/__getgrent.c b/libpwdgrp/__getgrent.c new file mode 100644 index 000000000..571da3fc3 --- /dev/null +++ b/libpwdgrp/__getgrent.c @@ -0,0 +1,204 @@ +/* + * __getgrent.c - This file is part of the libc-8086/grp package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include "grp.h" + +/* + * Define GR_SCALE_DYNAMIC if you want grp to dynamically scale its read buffer + * so that lines of any length can be used. On very very small systems, + * you may want to leave this undefined becasue it will make the grp functions + * somewhat larger (because of the inclusion of malloc and the code necessary). + * On larger systems, you will want to define this, because grp will _not_ + * deal with long lines gracefully (they will be skipped). + */ +#undef GR_SCALE_DYNAMIC + +#ifndef GR_SCALE_DYNAMIC +/* + * If scaling is not dynamic, the buffers will be statically allocated, and + * maximums must be chosen. GR_MAX_LINE_LEN is the maximum number of + * characters per line in the group file. GR_MAX_MEMBERS is the maximum + * number of members of any given group. + */ +#define GR_MAX_LINE_LEN 128 +/* GR_MAX_MEMBERS = (GR_MAX_LINE_LEN-(24+3+6))/9 */ +#define GR_MAX_MEMBERS 11 + +#endif /* !GR_SCALE_DYNAMIC */ + + +/* + * Define GR_DYNAMIC_GROUP_LIST to make initgroups() dynamically allocate + * space for it's GID array before calling setgroups(). This is probably + * unnecessary scalage, so it's undefined by default. + */ +#undef GR_DYNAMIC_GROUP_LIST + +#ifndef GR_DYNAMIC_GROUP_LIST +/* + * GR_MAX_GROUPS is the size of the static array initgroups() uses for + * its static GID array if GR_DYNAMIC_GROUP_LIST isn't defined. + */ +#define GR_MAX_GROUPS 64 + +#endif /* !GR_DYNAMIC_GROUP_LIST */ + + +/* + * This is the core group-file read function. It behaves exactly like + * getgrent() except that it is passed a file descriptor. getgrent() + * is just a wrapper for this function. + */ +struct group *__getgrent(int grp_fd) +{ +#ifndef GR_SCALE_DYNAMIC + static char line_buff[GR_MAX_LINE_LEN]; + static char *members[GR_MAX_MEMBERS]; +#else + static char *line_buff = NULL; + static char **members = NULL; + short line_index; + short buff_size; +#endif + static struct group group; + register char *ptr; + char *field_begin; + short member_num; + char *endptr; + int line_len; + + + /* We use the restart label to handle malformatted lines */ + restart: +#ifdef GR_SCALE_DYNAMIC + line_index = 0; + buff_size = 256; +#endif + +#ifndef GR_SCALE_DYNAMIC + /* Read the line into the static buffer */ + if ((line_len = read(grp_fd, line_buff, GR_MAX_LINE_LEN)) <= 0) + return NULL; + field_begin = strchr(line_buff, '\n'); + if (field_begin != NULL) + lseek(grp_fd, (long) (1 + field_begin - (line_buff + line_len)), + SEEK_CUR); + else { /* The line is too long - skip it :-\ */ + + do { + if ((line_len = read(grp_fd, line_buff, GR_MAX_LINE_LEN)) <= 0) + return NULL; + } while (!(field_begin = strchr(line_buff, '\n'))); + lseek(grp_fd, (long) ((field_begin - line_buff) - line_len + 1), + SEEK_CUR); + goto restart; + } + if (*line_buff == '#' || *line_buff == ' ' || *line_buff == '\n' || + *line_buff == '\t') + goto restart; + *field_begin = '\0'; + +#else /* !GR_SCALE_DYNAMIC */ + line_buff = realloc(line_buff, buff_size); + while (1) { + if ((line_len = read(grp_fd, line_buff + line_index, + buff_size - line_index)) <= 0) + return NULL; + field_begin = strchr(line_buff, '\n'); + if (field_begin != NULL) { + lseek(grp_fd, + (long) (1 + field_begin - + (line_len + line_index + line_buff)), SEEK_CUR); + *field_begin = '\0'; + if (*line_buff == '#' || *line_buff == ' ' + || *line_buff == '\n' || *line_buff == '\t') + goto restart; + break; + } else { /* Allocate some more space */ + + line_index = buff_size; + buff_size += 256; + line_buff = realloc(line_buff, buff_size); + } + } +#endif /* GR_SCALE_DYNAMIC */ + + /* Now parse the line */ + group.gr_name = line_buff; + ptr = strchr(line_buff, ':'); + if (ptr == NULL) + goto restart; + *ptr++ = '\0'; + + group.gr_passwd = ptr; + ptr = strchr(ptr, ':'); + if (ptr == NULL) + goto restart; + *ptr++ = '\0'; + + field_begin = ptr; + ptr = strchr(ptr, ':'); + if (ptr == NULL) + goto restart; + *ptr++ = '\0'; + + group.gr_gid = (gid_t) strtoul(field_begin, &endptr, 10); + if (*endptr != '\0') + goto restart; + + member_num = 0; + field_begin = ptr; + +#ifndef GR_SCALE_DYNAMIC + while ((ptr = strchr(ptr, ',')) != NULL) { + *ptr = '\0'; + ptr++; + members[member_num] = field_begin; + field_begin = ptr; + member_num++; + } + if (*field_begin == '\0') + members[member_num] = NULL; + else { + members[member_num] = field_begin; + members[member_num + 1] = NULL; + } +#else /* !GR_SCALE_DYNAMIC */ + if (members != NULL) + free(members); + members = (char **) malloc((member_num + 1) * sizeof(char *)); + for ( ; field_begin && *field_begin != '\0'; field_begin = ptr) { + if ((ptr = strchr(field_begin, ',')) != NULL) + *ptr++ = '\0'; + members[member_num++] = field_begin; + members = (char **) realloc(members, + (member_num + 1) * sizeof(char *)); + } + members[member_num] = NULL; +#endif /* GR_SCALE_DYNAMIC */ + + group.gr_mem = members; + return &group; +} diff --git a/libpwdgrp/__getpwent.c b/libpwdgrp/__getpwent.c new file mode 100644 index 000000000..e406b8824 --- /dev/null +++ b/libpwdgrp/__getpwent.c @@ -0,0 +1,115 @@ +/* + * __getpwent.c - This file is part of the libc-8086/pwd package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include +#include "pwd.h" + +#define PWD_BUFFER_SIZE 256 + +/* This isn't as flash as my previous version -- it doesn't dynamically + scale down the gecos on too-long lines, but it also makes fewer syscalls, + so it's probably nicer. Write me if you want the old version. Maybe I + should include it as a build-time option... ? + -Nat */ + +struct passwd *__getpwent(int pwd_fd) +{ + static char line_buff[PWD_BUFFER_SIZE]; + static struct passwd passwd; + char *field_begin; + char *endptr; + char *gid_ptr=NULL; + char *uid_ptr=NULL; + int line_len; + int i; + + /* We use the restart label to handle malformatted lines */ + restart: + /* Read the passwd line into the static buffer using a minimal of + syscalls. */ + if ((line_len = read(pwd_fd, line_buff, PWD_BUFFER_SIZE)) <= 0) + return NULL; + field_begin = strchr(line_buff, '\n'); + if (field_begin != NULL) + lseek(pwd_fd, (long) (1 + field_begin - (line_buff + line_len)), + SEEK_CUR); + else { /* The line is too long - skip it. :-\ */ + + do { + if ((line_len = read(pwd_fd, line_buff, PWD_BUFFER_SIZE)) <= 0) + return NULL; + } while (!(field_begin = strchr(line_buff, '\n'))); + lseek(pwd_fd, (long) (field_begin - line_buff) - line_len + 1, + SEEK_CUR); + goto restart; + } + if (*line_buff == '#' || *line_buff == ' ' || *line_buff == '\n' || + *line_buff == '\t') + goto restart; + *field_begin = '\0'; + + /* We've read the line; now parse it. */ + field_begin = line_buff; + for (i = 0; i < 7; i++) { + switch (i) { + case 0: + passwd.pw_name = field_begin; + break; + case 1: + passwd.pw_passwd = field_begin; + break; + case 2: + uid_ptr = field_begin; + break; + case 3: + gid_ptr = field_begin; + break; + case 4: + passwd.pw_gecos = field_begin; + break; + case 5: + passwd.pw_dir = field_begin; + break; + case 6: + passwd.pw_shell = field_begin; + break; + } + if (i < 6) { + field_begin = strchr(field_begin, ':'); + if (field_begin == NULL) + goto restart; + *field_begin++ = '\0'; + } + } + passwd.pw_gid = (gid_t) strtoul(gid_ptr, &endptr, 10); + if (*endptr != '\0') + goto restart; + + passwd.pw_uid = (uid_t) strtoul(uid_ptr, &endptr, 10); + if (*endptr != '\0') + goto restart; + + return &passwd; +} diff --git a/libpwdgrp/fgetgrent.c b/libpwdgrp/fgetgrent.c new file mode 100644 index 000000000..c5d63e05f --- /dev/null +++ b/libpwdgrp/fgetgrent.c @@ -0,0 +1,35 @@ +/* + * fgetgrent.c - This file is part of the libc-8086/grp package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include "grp.h" + +struct group *fgetgrent(FILE * file) +{ + if (file == NULL) { + errno = EINTR; + return NULL; + } + + return __getgrent(fileno(file)); +} diff --git a/libpwdgrp/fgetpwent.c b/libpwdgrp/fgetpwent.c new file mode 100644 index 000000000..6537600ff --- /dev/null +++ b/libpwdgrp/fgetpwent.c @@ -0,0 +1,35 @@ +/* + * fgetpwent.c - This file is part of the libc-8086/pwd package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include "pwd.h" + +struct passwd *fgetpwent(FILE * file) +{ + if (file == NULL) { + errno = EINTR; + return NULL; + } + + return __getpwent(fileno(file)); +} diff --git a/libpwdgrp/getgrgid.c b/libpwdgrp/getgrgid.c new file mode 100644 index 000000000..e70346a77 --- /dev/null +++ b/libpwdgrp/getgrgid.c @@ -0,0 +1,44 @@ +/* + * getgrgid.c - This file is part of the libc-8086/grp package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include "grp.h" + +struct group *getgrgid(const gid_t gid) +{ + struct group *group; + int grp_fd; + + if ((grp_fd = open("/etc/group", O_RDONLY)) < 0) + return NULL; + + while ((group = __getgrent(grp_fd)) != NULL) + if (group->gr_gid == gid) { + close(grp_fd); + return group; + } + + close(grp_fd); + return NULL; +} diff --git a/libpwdgrp/getgrnam.c b/libpwdgrp/getgrnam.c new file mode 100644 index 000000000..62b2b26ca --- /dev/null +++ b/libpwdgrp/getgrnam.c @@ -0,0 +1,50 @@ +/* + * getgrnam.c - This file is part of the libc-8086/grp package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include +#include "grp.h" + +struct group *getgrnam(const char *name) +{ + int grp_fd; + struct group *group; + + if (name == NULL) { + errno = EINVAL; + return NULL; + } + + if ((grp_fd = open("/etc/group", O_RDONLY)) < 0) + return NULL; + + while ((group = __getgrent(grp_fd)) != NULL) + if (!strcmp(group->gr_name, name)) { + close(grp_fd); + return group; + } + + close(grp_fd); + return NULL; +} diff --git a/libpwdgrp/getpw.c b/libpwdgrp/getpw.c new file mode 100644 index 000000000..ca11188aa --- /dev/null +++ b/libpwdgrp/getpw.c @@ -0,0 +1,47 @@ +/* + * getpw.c - This file is part of the libc-8086/pwd package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include "pwd.h" + +int getpw(uid_t uid, char *buf) +{ + struct passwd *passwd; + + if (buf == NULL) { + errno = EINVAL; + return -1; + } + if ((passwd = getpwuid(uid)) == NULL) + return -1; + + if (sprintf (buf, "%s:%s:%u:%u:%s:%s:%s", passwd->pw_name, passwd->pw_passwd, + passwd->pw_gid, passwd->pw_uid, passwd->pw_gecos, passwd->pw_dir, + passwd->pw_shell) < 0) { + errno = ENOBUFS; + return -1; + } + + return 0; +} diff --git a/libpwdgrp/getpwnam.c b/libpwdgrp/getpwnam.c new file mode 100644 index 000000000..88a31f8c2 --- /dev/null +++ b/libpwdgrp/getpwnam.c @@ -0,0 +1,51 @@ +/* + * getpwnam.c - This file is part of the libc-8086/pwd package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include +#include "pwd.h" + + +struct passwd *getpwnam(const char *name) +{ + int passwd_fd; + struct passwd *passwd; + + if (name == NULL) { + errno = EINVAL; + return NULL; + } + + if ((passwd_fd = open("/etc/passwd", O_RDONLY)) < 0) + return NULL; + + while ((passwd = __getpwent(passwd_fd)) != NULL) + if (!strcmp(passwd->pw_name, name)) { + close(passwd_fd); + return passwd; + } + + close(passwd_fd); + return NULL; +} diff --git a/libpwdgrp/getpwuid.c b/libpwdgrp/getpwuid.c new file mode 100644 index 000000000..776ed12da --- /dev/null +++ b/libpwdgrp/getpwuid.c @@ -0,0 +1,44 @@ +/* + * getpwuid.c - This file is part of the libc-8086/pwd package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include "pwd.h" + +struct passwd *getpwuid(uid_t uid) +{ + int passwd_fd; + struct passwd *passwd; + + if ((passwd_fd = open("/etc/passwd", O_RDONLY)) < 0) + return NULL; + + while ((passwd = __getpwent(passwd_fd)) != NULL) + if (passwd->pw_uid == uid) { + close(passwd_fd); + return passwd; + } + + close(passwd_fd); + return NULL; +} diff --git a/libpwdgrp/grent.c b/libpwdgrp/grent.c new file mode 100644 index 000000000..5b1cb6823 --- /dev/null +++ b/libpwdgrp/grent.c @@ -0,0 +1,54 @@ +/* + * grent.c - This file is part of the libc-8086/grp package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +/* + * setgrent(), endgrent(), and getgrent() are mutually-dependent functions, + * so they are all included in the same object file, and thus all linked + * in together. + */ + +#include "busybox.h" + +#include +#include +#include "grp.h" + +static int grp_fd = -1; + +void setgrent(void) +{ + if (grp_fd != -1) + close(grp_fd); + grp_fd = open("/etc/group", O_RDONLY); +} + +void endgrent(void) +{ + if (grp_fd != -1) + close(grp_fd); + grp_fd = -1; +} + +struct group *getgrent(void) +{ + if (grp_fd == -1) + return NULL; + return __getgrent(grp_fd); +} diff --git a/libpwdgrp/initgroups.c b/libpwdgrp/initgroups.c new file mode 100644 index 000000000..9b5dcbcb2 --- /dev/null +++ b/libpwdgrp/initgroups.c @@ -0,0 +1,115 @@ +/* + * initgroups.c - This file is part of the libc-8086/grp package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include "grp.h" + +/* + * Define GR_SCALE_DYNAMIC if you want grp to dynamically scale its read buffer + * so that lines of any length can be used. On very very small systems, + * you may want to leave this undefined becasue it will make the grp functions + * somewhat larger (because of the inclusion of malloc and the code necessary). + * On larger systems, you will want to define this, because grp will _not_ + * deal with long lines gracefully (they will be skipped). + */ +#undef GR_SCALE_DYNAMIC + +#ifndef GR_SCALE_DYNAMIC +/* + * If scaling is not dynamic, the buffers will be statically allocated, and + * maximums must be chosen. GR_MAX_LINE_LEN is the maximum number of + * characters per line in the group file. GR_MAX_MEMBERS is the maximum + * number of members of any given group. + */ +#define GR_MAX_LINE_LEN 128 +/* GR_MAX_MEMBERS = (GR_MAX_LINE_LEN-(24+3+6))/9 */ +#define GR_MAX_MEMBERS 11 + +#endif /* !GR_SCALE_DYNAMIC */ + + +/* + * Define GR_DYNAMIC_GROUP_LIST to make initgroups() dynamically allocate + * space for it's GID array before calling setgroups(). This is probably + * unnecessary scalage, so it's undefined by default. + */ +#undef GR_DYNAMIC_GROUP_LIST + +#ifndef GR_DYNAMIC_GROUP_LIST +/* + * GR_MAX_GROUPS is the size of the static array initgroups() uses for + * its static GID array if GR_DYNAMIC_GROUP_LIST isn't defined. + */ +#define GR_MAX_GROUPS 64 + +#endif /* !GR_DYNAMIC_GROUP_LIST */ + +int initgroups(__const char *user, gid_t gid) +{ + register struct group *group; + +#ifndef GR_DYNAMIC_GROUP_LIST + gid_t group_list[GR_MAX_GROUPS]; +#else + gid_t *group_list = NULL; +#endif + register char **tmp_mem; + int num_groups; + int grp_fd; + + + if ((grp_fd = open("/etc/group", O_RDONLY)) < 0) + return -1; + + num_groups = 0; +#ifdef GR_DYNAMIC_GROUP_LIST + group_list = (gid_t *) realloc(group_list, 1); +#endif + group_list[num_groups] = gid; +#ifndef GR_DYNAMIC_GROUP_LIST + while (num_groups < GR_MAX_GROUPS && + (group = __getgrent(grp_fd)) != NULL) +#else + while ((group = __getgrent(grp_fd)) != NULL) +#endif + { + if (group->gr_gid != gid); + { + tmp_mem = group->gr_mem; + while (*tmp_mem != NULL) { + if (!strcmp(*tmp_mem, user)) { + num_groups++; +#ifdef GR_DYNAMIC_GROUP_LIST + group_list = (gid_t *) realloc(group_list, num_groups * + sizeof(gid_t *)); +#endif + group_list[num_groups] = group->gr_gid; + } + tmp_mem++; + } + } + } + close(grp_fd); + return setgroups(num_groups, group_list); +} diff --git a/libpwdgrp/putpwent.c b/libpwdgrp/putpwent.c new file mode 100644 index 000000000..88dffc952 --- /dev/null +++ b/libpwdgrp/putpwent.c @@ -0,0 +1,39 @@ +/* + * putpwent.c - This file is part of the libc-8086/pwd package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include "pwd.h" + +int putpwent(const struct passwd *passwd, FILE * f) +{ + if (passwd == NULL || f == NULL) { + errno = EINVAL; + return -1; + } + if (fprintf (f, "%s:%s:%u:%u:%s:%s:%s\n", passwd->pw_name, passwd->pw_passwd, + passwd->pw_gid, passwd->pw_uid, passwd->pw_gecos, passwd->pw_dir, + passwd->pw_shell) < 0) + return -1; + + return 0; +} diff --git a/libpwdgrp/pwent.c b/libpwdgrp/pwent.c new file mode 100644 index 000000000..84bd6176b --- /dev/null +++ b/libpwdgrp/pwent.c @@ -0,0 +1,58 @@ +/* + * pwent.c - This file is part of the libc-8086/pwd package for ELKS, + * Copyright (C) 1995, 1996 Nat Friedman . + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "busybox.h" + +#include +#include +#include +#include "pwd.h" +#include + +/* + * setpwent(), endpwent(), and getpwent() are included in the same object + * file, since one cannot be used without the other two, so it makes sense to + * link them all in together. + */ + +/* file descriptor for the password file currently open */ +static int pw_fd = -1; + +void setpwent(void) +{ + if (pw_fd != -1) + close(pw_fd); + + pw_fd = open("/etc/passwd", O_RDONLY); +} + +void endpwent(void) +{ + if (pw_fd != -1) + close(pw_fd); + pw_fd = -1; +} + +struct passwd *getpwent(void) +{ + if (pw_fd != -1) + return (__getpwent(pw_fd)); + return NULL; +} diff --git a/libpwdgrp/setgroups.c b/libpwdgrp/setgroups.c new file mode 100644 index 000000000..c9b86f016 --- /dev/null +++ b/libpwdgrp/setgroups.c @@ -0,0 +1,42 @@ +/* vi: set sw=4 ts=4: */ +/* + * Taken from the set of syscalls for uClibc + * + * Copyright (C) 1999,2000,2001 by Erik Andersen + * , + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU Library General Public License as published by + * the Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License + * for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include "busybox.h" + +#include +#include +#include +#include +/* Kernel headers before 2.1.mumble need this on the Alpha to get + _syscall* defined. */ +#define __LIBRARY__ +#include +#if __GNU_LIBRARY__ < 5 +/* This is needed for libc5 */ +#include +#endif +#include "grp.h" + +//#define __NR_setgroups 81 +_syscall2(int, setgroups, size_t, size, const gid_t *, list); + diff --git a/libpwdgrp/shadow.c b/libpwdgrp/shadow.c new file mode 100644 index 000000000..6cf195e4a --- /dev/null +++ b/libpwdgrp/shadow.c @@ -0,0 +1,302 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1994, Julianne Frances Haugh + * , + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* TODO: fgetspent_r.c getspent_r.c getspnam_r.c sgetspent_r.c + * lckpwdf ulckpwdf + */ + +#include "busybox.h" + +#include +#include +#include +#include + +#include "shadow.h" + +static FILE *shadow; +static char spwbuf[BUFSIZ]; +static struct spwd spwd; + +#define FIELDS 9 +#define OFIELDS 5 + +/* setspent - initialize access to shadow text and DBM files */ +void setspent(void) +{ + if (shadow) { + rewind(shadow); + } else { + if ((shadow = fopen("/etc/shadow", "r")) == NULL) + perror_msg_and_die("/etc/shadow"); + } +} + +/* endspent - terminate access to shadow text and DBM files */ +void endspent(void) +{ + if (shadow) + (void) fclose(shadow); + shadow = (FILE *) 0; +} + +/* getspent - get a (struct spwd *) from the current shadow file */ +struct spwd *getspent(void) +{ + if (!shadow) + setspent(); + return (fgetspent(shadow)); +} + +/* getspnam - get a shadow entry by name */ +struct spwd *getspnam(const char *name) +{ + struct spwd *sp; + + if (!name || !strlen(name)) + return NULL; + + setspent(); + while ((sp = getspent()) != NULL) { + if (strcmp(name, sp->sp_namp) == 0) + break; + } + endspent(); + return (sp); +} + + +/* sgetspent - convert string in shadow file format to (struct spwd *) */ +/* returns NULL on error */ +struct spwd *sgetspent(const char *string) +{ + char *fields[FIELDS]; + char *cp; + char *cpp; + int i; + + /* + * Copy string to local buffer. It has to be tokenized and we + * have to do that to our private copy. + */ + + if (strlen(string) >= sizeof spwbuf) + /* return 0; */ + return NULL; + strcpy(spwbuf, string); + + if ((cp = strrchr(spwbuf, '\n'))) + *cp = '\0'; + + /* + * Tokenize the string into colon separated fields. Allow up to + * FIELDS different fields. + */ + + for (cp = spwbuf, i = 0; *cp && i < FIELDS; i++) { + fields[i] = cp; + while (*cp && *cp != ':') + cp++; + + if (*cp) + *cp++ = '\0'; + } + + /* + * It is acceptable for the last SVR4 field to be blank. This + * results in the loop being terminated early. In which case, + * we just make the last field be blank and be done with it. + */ + + if (i == (FIELDS - 1)) + fields[i++] = cp; + + if ((cp && *cp) || (i != FIELDS && i != OFIELDS)) + /* return 0; */ + return NULL; + + /* + * Start populating the structure. The fields are all in + * static storage, as is the structure we pass back. If we + * ever see a name with '+' as the first character, we try + * to turn on NIS processing. + */ + + spwd.sp_namp = fields[0]; + spwd.sp_pwdp = fields[1]; + + /* + * Get the last changed date. For all of the integer fields, + * we check for proper format. It is an error to have an + * incorrectly formatted number, unless we are using NIS. + */ + + if ((spwd.sp_lstchg = strtol(fields[2], &cpp, 10)) == 0 && *cpp) { + /* return 0; */ + return NULL; + } else if (fields[2][0] == '\0') + spwd.sp_lstchg = -1; + + /* + * Get the minimum period between password changes. + */ + + if ((spwd.sp_min = strtol(fields[3], &cpp, 10)) == 0 && *cpp) { + /* return 0; */ + return NULL; + } else if (fields[3][0] == '\0') + spwd.sp_min = -1; + + /* + * Get the maximum number of days a password is valid. + */ + + if ((spwd.sp_max = strtol(fields[4], &cpp, 10)) == 0 && *cpp) { + /* return 0; */ + return NULL; + } else if (fields[4][0] == '\0') + spwd.sp_max = -1; + + /* + * If there are only OFIELDS fields (this is a SVR3.2 /etc/shadow + * formatted file), initialize the other field members to -1. + */ + + if (i == OFIELDS) { + spwd.sp_warn = spwd.sp_inact = spwd.sp_expire = spwd.sp_flag = -1; + + return &spwd; + } + + /* + * The rest of the fields are mandatory for SVR4, but optional + * for anything else. However, if one is present the others + * must be as well. + */ + + /* + * Get the number of days of password expiry warning. + */ + + if ((spwd.sp_warn = strtol(fields[5], &cpp, 10)) == 0 && *cpp) { + /* return 0; */ + return NULL; + } else if (fields[5][0] == '\0') + spwd.sp_warn = -1; + + /* + * Get the number of days of inactivity before an account is + * disabled. + */ + + if ((spwd.sp_inact = strtol(fields[6], &cpp, 10)) == 0 && *cpp) { + /* return 0; */ + return NULL; + } else if (fields[6][0] == '\0') + spwd.sp_inact = -1; + + /* + * Get the number of days after the epoch before the account is + * set to expire. + */ + + if ((spwd.sp_expire = strtol(fields[7], &cpp, 10)) == 0 && *cpp) { + /* return 0; */ + return NULL; + } else if (fields[7][0] == '\0') + spwd.sp_expire = -1; + + /* + * This field is reserved for future use. But it isn't supposed + * to have anything other than a valid integer in it. + */ + + if ((spwd.sp_flag = strtol(fields[8], &cpp, 10)) == 0 && *cpp) { + /* return 0; */ + return NULL; + } else if (fields[8][0] == '\0') + spwd.sp_flag = -1; + + return (&spwd); +} + +/* fgetspent - get an entry from an /etc/shadow formatted stream */ +struct spwd *fgetspent(FILE *fp) +{ + char buf[BUFSIZ]; + char *cp; + + if (!fp) + /* return (0); */ + return NULL; + + if (fgets(buf, sizeof buf, fp) != (char *) 0) { + if ((cp = strchr(buf, '\n'))) + *cp = '\0'; + return (sgetspent(buf)); + } + /* return 0; */ + return NULL; +} + +/* + * putspent - put a (struct spwd *) into the (FILE *) you provide. + * + * this was described in shadow_.h but not implemented, so here + * I go. -beppu + * + */ +int putspent(const struct spwd *sp, FILE *fp) +{ + int ret; + + /* seek to end */ + ret = fseek(fp, 0, SEEK_END); + if (ret == -1) { + /* return -1; */ + return 1; + } + + /* powered by fprintf */ + fprintf(fp, "%s:%s:%ld:%ld:%ld:%ld:%ld:%ld:%s\n", sp->sp_namp, /* login name */ + sp->sp_pwdp, /* encrypted password */ + sp->sp_lstchg, /* date of last change */ + sp->sp_min, /* minimum number of days between changes */ + sp->sp_max, /* maximum number of days between changes */ + sp->sp_warn, /* number of days of warning before password expires */ + sp->sp_inact, /* number of days after password expires until + the account becomes unusable */ + sp->sp_expire, /* days since 1/1/70 until account expires */ + ""); + return 0; +} + + diff --git a/loginutils/Makefile b/loginutils/Makefile new file mode 100644 index 000000000..09b368afc --- /dev/null +++ b/loginutils/Makefile @@ -0,0 +1,30 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2002 Erik Andersen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +TOPDIR:= ../ +LOGINUTILS_DIR:=./ +include $(TOPDIR).config +include $(TOPDIR)Rules.mak +include Makefile.in +all: $(libraries-y) +-include $(TOPDIR).depend + +clean: + rm -f *.o *.a $(AR_TARGET) + diff --git a/loginutils/Makefile.in b/loginutils/Makefile.in new file mode 100644 index 000000000..adee35baa --- /dev/null +++ b/loginutils/Makefile.in @@ -0,0 +1,45 @@ +# Makefile for busybox +# +# Copyright (C) 1999-2002 Erik Andersen +# +# This program is free software; you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation; either version 2 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +# General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +# + +LOGINUTILS_AR:=loginutils.a +ifndef LOGINUTILS_DIR +LOGINUTILS_DIR:=$(TOPDIR)loginutils/ +endif + +LOGINUTILS-y:= +LOGINUTILS-$(CONFIG_ADDGROUP) += addgroup.o +LOGINUTILS-$(CONFIG_ADDUSER) += adduser.o +LOGINUTILS-$(CONFIG_DELUSER) += deluser.o +LOGINUTILS-$(CONFIG_GETTY) += getty.o +LOGINUTILS-$(CONFIG_LOGIN) += login.o tinylogin.o +LOGINUTILS-$(CONFIG_SU) += su.o tinylogin.o + +libraries-y+=$(LOGINUTILS_DIR)$(LOGINUTILS_AR) + +needcrypt-y:= +needcrypt-$(CONFIG_LOGIN) := y +needcrypt-$(CONFIG_SU) := y + +ifeq ($(needcrypt-y),y) + libraries-y +=-lcrypt +endif + +$(LOGINUTILS_DIR)$(LOGINUTILS_AR): $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y)) + $(AR) -ro $@ $(patsubst %,$(LOGINUTILS_DIR)%, $(LOGINUTILS-y)) + 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 @@ +/* vi: set sw=4 ts=4: */ +/* + * addgroup - add users to /etc/passwd and /etc/shadow + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "pwd.h" +#include "grp.h" + +#define GROUP_FILE "/etc/group" +#define SHADOW_FILE "/etc/gshadow" + + +/* structs __________________________ */ + +/* data _____________________________ */ + +/* defaults : should this be in an external file? */ +static const char default_passwd[] = "x"; + + +/* make sure gr_name isn't taken, make sure gid is kosher + * return 1 on failure */ +static int group_study(const char *filename, struct group *g) +{ + FILE *etc_group; + gid_t desired; + + struct group *grp; + const int max = 65000; + + etc_group = xfopen(filename, "r"); + + /* make sure gr_name isn't taken, make sure gid is kosher */ + desired = g->gr_gid; + while ((grp = fgetgrent(etc_group))) { + if ((strcmp(grp->gr_name, g->gr_name)) == 0) { + error_msg_and_die("%s: group already in use\n", g->gr_name); + } + if ((desired) && grp->gr_gid == desired) { + error_msg_and_die("%d: gid has already been allocated\n", + desired); + } + if ((grp->gr_gid > g->gr_gid) && (grp->gr_gid < max)) { + g->gr_gid = grp->gr_gid; + } + } + fclose(etc_group); + + /* gid */ + if (desired) { + g->gr_gid = desired; + } else { + g->gr_gid++; + } + /* return 1; */ + return 0; +} + +/* append a new user to the passwd file */ +static int addgroup(const char *filename, char *group, gid_t gid) +{ + FILE *etc_group; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + FILE *etc_gshadow; + char *gshadow = SHADOW_FILE; +#endif + + struct group gr; + + /* group:passwd:gid:userlist */ + static const char entryfmt[] = "%s:%s:%d:%s\n"; + + /* make sure gid and group haven't already been allocated */ + gr.gr_gid = gid; + gr.gr_name = group; + if (group_study(filename, &gr)) + return 1; + + /* add entry to group */ + etc_group = xfopen(filename, "a"); + + fprintf(etc_group, entryfmt, group, default_passwd, gr.gr_gid, ""); + fclose(etc_group); + + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + /* add entry to gshadow if necessary */ + if (access(gshadow, F_OK|W_OK) == 0) { + etc_gshadow = xfopen(gshadow, "a"); + fprintf(etc_gshadow, "%s:!::\n", group); + fclose(etc_gshadow); + } +#endif + + /* return 1; */ + return 0; +} + +/* + * addgroup will take a login_name as its first parameter. + * + * gid + * + * can be customized via command-line parameters. + * ________________________________________________________________________ */ +int addgroup_main(int argc, char **argv) +{ + int opt; + char *group; + gid_t gid = 0; + + /* get remaining args */ + while ((opt = getopt (argc, argv, "g:")) != -1) + switch (opt) { + case 'g': + gid = strtol(optarg, NULL, 10); + break; + default: + show_usage(); + break; + } + + if (optind >= argc) { + show_usage(); + } else { + group = argv[optind]; + } + + if (geteuid() != 0) { + error_msg_and_die + ("Only root may add a group to the system."); + } + + /* werk */ + return addgroup(GROUP_FILE, group, gid); +} + +/* $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 @@ +/* vi: set sw=4 ts=4: */ +/* + * adduser - add users to /etc/passwd and /etc/shadow + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" +#include "pwd.h" +#include "grp.h" + +#define PASSWD_FILE "/etc/passwd" +#define SHADOW_FILE "/etc/shadow" + + +/* structs __________________________ */ + +typedef struct { + uid_t u; + gid_t g; +} Id; + +/* data _____________________________ */ + +/* defaults : should this be in an external file? */ +static const char default_passwd[] = "x"; +static const char default_gecos[] = "Linux User,,,"; +static const char default_home_prefix[] = "/home"; +static const char default_shell[] = "/bin/sh"; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + +#include "shadow.h" + +/* shadow in use? */ +static int shadow_enabled = 0; +#endif + +/* remix */ +/* EDR recoded such that the uid may be passed in *p */ +static int passwd_study(const char *filename, struct passwd *p) +{ + struct passwd *pw; + FILE *passwd; + + const int min = 500; + const int max = 65000; + + passwd = wfopen(filename, "r"); + if (!passwd) + return 4; + + /* EDR if uid is out of bounds, set to min */ + if ((p->pw_uid > max) || (p->pw_uid < min)) + p->pw_uid = min; + + /* stuff to do: + * make sure login isn't taken; + * find free uid and gid; + */ + while ((pw = fgetpwent(passwd))) { + if (strcmp(pw->pw_name, p->pw_name) == 0) { + /* return 0; */ + return 1; + } + if ((pw->pw_uid >= p->pw_uid) && (pw->pw_uid < max) + && (pw->pw_uid >= min)) { + p->pw_uid = pw->pw_uid + 1; + } + } + + /* EDR check for an already existing gid */ + while (getgrgid(p->pw_uid) != NULL) + p->pw_uid++; + + /* EDR also check for an existing group definition */ + if (getgrnam(p->pw_name) != NULL) + return 3; + + /* EDR bounds check */ + if ((p->pw_uid > max) || (p->pw_uid < min)) + return 2; + + /* EDR create new gid always = uid */ + p->pw_gid = p->pw_uid; + + /* return 1; */ + return 0; +} + +static void addgroup_wrapper(const char *login, gid_t gid) +{ + int argc = 3; + const char *argv0_save; + char group_id[8]; + char group_name[32]; + char *argv[] = { group_name, "-g", group_id }; + + argv0_save = applet_name; + applet_name = "addgroup"; + safe_strncpy(group_name, login, 32); + sprintf(group_id, "%d", gid); + addgroup_main(argc, argv); + applet_name = argv0_save; +} + +static void passwd_wrapper(const char *login) +{ + static const char prog[] = "passwd"; + execlp(prog, prog, login, NULL); + error_msg_and_die("Failed to execute 'passwd', you must set the password for '%s' manually", login); +} + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS +/* + * pwd_to_spwd - create entries for new spwd structure + * + * pwd_to_spwd() creates a new (struct spwd) containing the + * information in the pointed-to (struct passwd). + */ +#define DAY (24L*3600L) +#define WEEK (7*DAY) +#define SCALE DAY +static struct spwd *pwd_to_spwd(const struct passwd *pw) +{ + static struct spwd sp; + + /* + * Nice, easy parts first. The name and passwd map directly + * from the old password structure to the new one. + */ + sp.sp_namp = pw->pw_name; + sp.sp_pwdp = pw->pw_passwd; + + /* + * Defaults used if there is no pw_age information. + */ + sp.sp_min = 0; + sp.sp_max = (10000L * DAY) / SCALE; + sp.sp_lstchg = time((time_t *) 0) / SCALE; + + /* + * These fields have no corresponding information in the password + * file. They are set to uninitialized values. + */ + sp.sp_warn = -1; + sp.sp_expire = -1; + sp.sp_inact = -1; + sp.sp_flag = -1; + + return &sp; +} +#endif + +/* putpwent(3) remix */ +static int adduser(const char *filename, struct passwd *p) +{ + FILE *passwd; + int r; +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + FILE *shadow; + struct spwd *sp; +#endif + + /* make sure everything is kosher and setup uid && gid */ + passwd = wfopen(filename, "a"); + if (passwd == NULL) { + /* return -1; */ + return 1; + } + fseek(passwd, 0, SEEK_END); + + /* if (passwd_study(filename, p) == 0) { */ + r = passwd_study(filename, p); + if (r) { + if (r == 1) + error_msg("%s: login already in use", p->pw_name); + else if (r == 2) + error_msg("illegal uid or no uids left"); + else if (r == 3) + error_msg("group name %s already in use", p->pw_name); + else + error_msg("generic error."); + /* return -1; */ + return 1; + } + + /* add to passwd */ + if (putpwent(p, passwd) == -1) { + /* return -1; */ + return 1; + } + fclose(passwd); + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + /* add to shadow if necessary */ + if (shadow_enabled) { + shadow = wfopen(SHADOW_FILE, "a"); + if (shadow == NULL) { + /* return -1; */ + return 1; + } + fseek(shadow, 0, SEEK_END); + sp = pwd_to_spwd(p); + sp->sp_max = 99999; /* debianish */ + sp->sp_warn = 7; + fprintf(shadow, "%s:!:%ld:%ld:%ld:%ld:::\n", + sp->sp_namp, sp->sp_lstchg, sp->sp_min, sp->sp_max, + sp->sp_warn); + fclose(shadow); + } +#endif + + /* add to group */ + /* addgroup should be responsible for dealing w/ gshadow */ + addgroup_wrapper(p->pw_name, p->pw_gid); + + /* Clear the umask for this process so it doesn't + * * screw up the permissions on the mkdir and chown. */ + umask(0); + + /* mkdir */ + if (mkdir(p->pw_dir, 0755)) { + perror_msg("%s", p->pw_dir); + } + /* Set the owner and group so it is owned by the new user. */ + if (chown(p->pw_dir, p->pw_uid, p->pw_gid)) { + perror_msg("%s", p->pw_dir); + } + /* Now fix up the permissions to 2755. Can't do it before now + * since chown will clear the setgid bit */ + if (chmod(p->pw_dir, 02755)) { + perror_msg("%s", p->pw_dir); + } + /* interactively set passwd */ + passwd_wrapper(p->pw_name); + + return 0; +} + + +/* return current uid (root is always uid == 0, right?) */ +static inline uid_t i_am_not_root(void) +{ + return geteuid(); +} + +/* + * adduser will take a login_name as its first parameter. + * + * home + * shell + * gecos + * + * can be customized via command-line parameters. + * ________________________________________________________________________ */ +int adduser_main(int argc, char **argv) +{ + int i = 0; + char opt; + const char *login; + const char *gecos; + const char *home = NULL; + const char *shell; + + struct passwd pw; + + /* init */ + if (argc < 2) { + show_usage(); + } + gecos = default_gecos; + shell = default_shell; + + /* get args */ + while ((opt = getopt (argc, argv, "h:g:s:")) != -1) + switch (opt) { + case 'h': + home = argv[++i]; + break; + case 'g': + gecos = argv[++i]; + break; + case 's': + shell = argv[++i]; + break; + default: + show_usage (); + break; + } + + /* got root? */ + if (i_am_not_root()) { + error_msg_and_die( "Only root may add a user or group to the system."); + } + + /* get login */ + if (optind >= argc) { + error_msg_and_die( "no user specified"); + } + login = argv[optind]; + + /* create string for $HOME if not specified already */ + if (!home) { + home = concat_path_file(default_home_prefix, login); + } +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + /* is /etc/shadow in use? */ + shadow_enabled = (0 == access(SHADOW_FILE, F_OK)); +#endif + + /* create a passwd struct */ + pw.pw_name = (char *)login; + pw.pw_passwd = (char *)default_passwd; + pw.pw_uid = 0; + pw.pw_gid = 0; + pw.pw_gecos = (char *)gecos; + pw.pw_dir = (char *)home; + pw.pw_shell = (char *)shell; + + /* grand finale */ + return adduser(PASSWD_FILE, &pw); +} + +/* $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 @@ +# +# For a description of the syntax of this configuration file, +# see scripts/kbuild/config-language.txt. +# + +mainmenu_option next_comment +comment 'Login/Password Management Utilities' + + +bool 'addgroup' CONFIG_ADDGROUP +bool 'adduser' CONFIG_ADDUSER +bool 'deluser' CONFIG_DELUSER +bool 'delgroup' CONFIG_DELUSER +bool 'getty' CONFIG_GETTY +bool 'login' CONFIG_LOGIN +if [ "$CONFIG_LOGIN" = "y" ]; then + bool ' Support for /etc/securetty' CONFIG_FEATURE_SECURETTY +fi +bool 'su' CONFIG_SU +if [ "$CONFIG_ADDUSER" = "y" -o "$CONFIG_DELUSER" = "y" -o "$CONFIG_LOGIN" = "y" -o "$CONFIG_SU" = "y" ]; then + bool 'Support for shadow passwords' CONFIG_FEATURE_SHADOWPASSWDS +fi + +endmenu + 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 @@ +/* vi: set sw=4 ts=4: */ +/* + * deluser (remove lusers from the system ;) for TinyLogin + * + * Copyright (C) 1999 by Lineo, inc. and John Beppu + * Copyright (C) 1999,2000,2001 by John Beppu + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + */ + +#include +#include +#include +#include +#include +#include "busybox.h" + +#define PASSWD_FILE "/etc/passwd" +#define GROUP_FILE "/etc/group" +#define SHADOW_FILE "/etc/shadow" +#define GSHADOW_FILE "/etc/gshadow" + + +/* where to start and stop deletion */ +typedef struct { + size_t start; + size_t stop; +} Bounds; + +/* An interesting side-effect of boundary()'s + * implementation is that the first user (typically root) + * cannot be removed. Let's call it a feature. */ +static inline Bounds boundary(const char *buffer, const char *login) +{ + char needle[256]; + char *start; + char *stop; + Bounds b; + + snprintf(needle, 256, "\n%s:", login); + needle[255] = 0; + start = strstr(buffer, needle); + if (!start) { + b.start = 0; + b.stop = 0; + return b; + } + start++; + + stop = index(start, '\n'); /* index is a BSD-ism */ + b.start = start - buffer; + b.stop = stop - buffer; + return b; +} + +/* grep -v ^login (except it only deletes the first match) */ +/* ...in fact, I think I'm going to simplify this later */ +static int del_line_matching(const char *login, const char *filename) +{ + char *buffer; + FILE *passwd; + size_t len; + Bounds b; + struct stat statbuf; + + /* load into buffer */ + passwd = fopen(filename, "r"); + if (!passwd) { + return 1; + } + stat(filename, &statbuf); + len = statbuf.st_size; + buffer = (char *) malloc(len * sizeof(char)); + + if (!buffer) { + fclose(passwd); + return 1; + } + fread(buffer, len, sizeof(char), passwd); + + fclose(passwd); + + /* find the user to remove */ + b = boundary(buffer, login); + if (b.stop == 0) { + free(buffer); + return 1; + } + + /* write the file w/o the user */ + passwd = fopen(filename, "w"); + if (!passwd) { + return 1; + } + fwrite(buffer, (b.start - 1), sizeof(char), passwd); + fwrite(&buffer[b.stop], (len - b.stop), sizeof(char), passwd); + + fclose(passwd); + + return 0; +} + +/* ________________________________________________________________________ */ +int delgroup_main(int argc, char **argv) +{ + /* int successful; */ + int failure; + + if (argc != 2) { + show_usage(); + } else { + + failure = del_line_matching(argv[1], GROUP_FILE); +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + if (access(GSHADOW_FILE, W_OK) == 0) { + /* EDR the |= works if the error is not 0, so he had it wrong */ + failure |= del_line_matching(argv[1], GSHADOW_FILE); + } +#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ + /* if (!successful) { */ + if (failure) { + error_msg_and_die("%s: Group could not be removed\n", argv[1]); + } + + } + return (EXIT_SUCCESS); +} + +/* ________________________________________________________________________ */ +int deluser_main(int argc, char **argv) +{ + /* int successful; */ + int failure; + + if (argc != 2) { + show_usage(); + } else { + + failure = del_line_matching(argv[1], PASSWD_FILE); + /* if (!successful) { */ + if (failure) { + error_msg_and_die("%s: User could not be removed from %s\n", + argv[1], PASSWD_FILE); + } +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + failure = del_line_matching(argv[1], SHADOW_FILE); + /* if (!successful) { */ + if (failure) { + error_msg_and_die("%s: User could not be removed from %s\n", + argv[1], SHADOW_FILE); + } + failure = del_line_matching(argv[1], GSHADOW_FILE); + /* if (!successful) { */ + if (failure) { + error_msg_and_die("%s: User could not be removed from %s\n", + argv[1], GSHADOW_FILE); + } +#endif /* CONFIG_FEATURE_SHADOWPASSWDS */ + failure = del_line_matching(argv[1], GROUP_FILE); + /* if (!successful) { */ + if (failure) { + error_msg_and_die("%s: User could not be removed from %s\n", + argv[1], GROUP_FILE); + } + + } + return (EXIT_SUCCESS); +} + +/* $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 @@ +/* vi: set sw=4 ts=4: */ +/* agetty.c - another getty program for Linux. By W. Z. Venema 1989 + Ported to Linux by Peter Orbaek + This program is freely distributable. The entire man-page used to + be here. Now read the real man-page agetty.8 instead. + + -f option added by Eric Rasmussen - 12/28/95 + + 1999-02-22 Arkadiusz Mi¶kiewicz + - added Native Language Support + + 1999-05-05 Thorsten Kranzkowski + - enable hardware flow control before displaying /etc/issue + +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#define _PATH_LOGIN "/bin/login" + +#ifdef linux +#include +#define USE_SYSLOG +#endif + +extern void updwtmp(const char *filename, const struct utmp *ut); + + /* If USE_SYSLOG is undefined all diagnostics go directly to /dev/console. */ +#ifdef USE_SYSLOG +#include +#endif + + + /* + * Some heuristics to find out what environment we are in: if it is not + * System V, assume it is SunOS 4. + */ + +#ifdef LOGIN_PROCESS /* defined in System V utmp.h */ +#define SYSV_STYLE /* select System V style getty */ +#endif + + /* + * Things you may want to modify. + * + * If ISSUE is not defined, agetty will never display the contents of the + * /etc/issue file. You will not want to spit out large "issue" files at the + * wrong baud rate. Relevant for System V only. + * + * You may disagree with the default line-editing etc. characters defined + * below. Note, however, that DEL cannot be used for interrupt generation + * and for line editing at the same time. + */ + +#ifdef SYSV_STYLE +#define ISSUE "/etc/issue" /* displayed before the login prompt */ +#include +#include +#endif + +#define LOGIN " login: " /* login prompt */ + +/* Some shorthands for control characters. */ + +#define CTL(x) (x ^ 0100) /* Assumes ASCII dialect */ +#define CR CTL('M') /* carriage return */ +#define NL CTL('J') /* line feed */ +#define BS CTL('H') /* back space */ +#define DEL CTL('?') /* delete */ + +/* Defaults for line-editing etc. characters; you may want to change this. */ + +#define DEF_ERASE DEL /* default erase character */ +#define DEF_INTR CTL('C') /* default interrupt character */ +#define DEF_QUIT CTL('\\') /* default quit char */ +#define DEF_KILL CTL('U') /* default kill char */ +#define DEF_EOF CTL('D') /* default EOF char */ +#define DEF_EOL 0 +#define DEF_SWITCH 0 /* default switch char */ + + /* + * SunOS 4.1.1 termio is broken. We must use the termios stuff instead, + * because the termio -> termios translation does not clear the termios + * CIBAUD bits. Therefore, the tty driver would sometimes report that input + * baud rate != output baud rate. I did not notice that problem with SunOS + * 4.1. We will use termios where available, and termio otherwise. + */ + +/* linux 0.12 termio is broken too, if we use it c_cc[VERASE] isn't set + properly, but all is well if we use termios?! */ + +#ifdef TCGETS +#undef TCGETA +#undef TCSETA +#undef TCSETAW +#define termio termios +#define TCGETA TCGETS +#define TCSETA TCSETS +#define TCSETAW TCSETSW +#endif + + /* + * This program tries to not use the standard-i/o library. This keeps the + * executable small on systems that do not have shared libraries (System V + * Release <3). + */ +#ifndef BUFSIZ +#define BUFSIZ 1024 +#endif + + /* + * When multiple baud rates are specified on the command line, the first one + * we will try is the first one specified. + */ + +#define FIRST_SPEED 0 + +/* Storage for command-line options. */ + +#define MAX_SPEED 10 /* max. nr. of baud rates */ + +struct options { + int flags; /* toggle switches, see below */ + int timeout; /* time-out period */ + char *login; /* login program */ + char *tty; /* name of tty */ + char *initstring; /* modem init string */ + char *issue; /* alternative issue file */ + int numspeed; /* number of baud rates to try */ + int speeds[MAX_SPEED]; /* baud rates to be tried */ +}; + +#define F_PARSE (1<<0) /* process modem status messages */ +#define F_ISSUE (1<<1) /* display /etc/issue */ +#define F_RTSCTS (1<<2) /* enable RTS/CTS flow control */ +#define F_LOCAL (1<<3) /* force local */ +#define F_INITSTRING (1<<4) /* initstring is set */ +#define F_WAITCRLF (1<<5) /* wait for CR or LF */ +#define F_CUSTISSUE (1<<6) /* give alternative issue file */ +#define F_NOPROMPT (1<<7) /* don't ask for login name! */ + +/* Storage for things detected while the login name was read. */ + +struct chardata { + int erase; /* erase character */ + int kill; /* kill character */ + int eol; /* end-of-line character */ + int parity; /* what parity did we see */ + int capslock; /* upper case without lower case */ +}; + +/* Initial values for the above. */ + +struct chardata init_chardata = { + DEF_ERASE, /* default erase character */ + DEF_KILL, /* default kill character */ + 13, /* default eol char */ + 0, /* space parity */ + 0, /* no capslock */ +}; + +struct Speedtab { + long speed; + int code; +}; + +static struct Speedtab speedtab[] = { + {50, B50}, + {75, B75}, + {110, B110}, + {134, B134}, + {150, B150}, + {200, B200}, + {300, B300}, + {600, B600}, + {1200, B1200}, + {1800, B1800}, + {2400, B2400}, + {4800, B4800}, + {9600, B9600}, +#ifdef B19200 + {19200, B19200}, +#endif +#ifdef B38400 + {38400, B38400}, +#endif +#ifdef EXTA + {19200, EXTA}, +#endif +#ifdef EXTB + {38400, EXTB}, +#endif +#ifdef B57600 + {57600, B57600}, +#endif +#ifdef B115200 + {115200, B115200}, +#endif +#ifdef B230400 + {230400, B230400}, +#endif + {0, 0}, +}; + +static void parse_args(int argc, char **argv, struct options *op); +static void parse_speeds(struct options *op, char *arg); +static void update_utmp(char *line); +static void open_tty(char *tty, struct termio *tp, int local); +static void termio_init(struct termio *tp, int speed, struct options *op); +static void auto_baud(struct termio *tp); +static void do_prompt(struct options *op, struct termio *tp); +static void next_speed(struct termio *tp, struct options *op); +static char *get_logname(struct options *op, struct chardata *cp, + + struct termio *tp); +static void termio_final(struct options *op, struct termio *tp, + + struct chardata *cp); +static int caps_lock(const char *s); +static int bcode(const char *s); +static void error(const char *fmt, ...); + +/* The following is used for understandable diagnostics. */ + +/* Fake hostname for ut_host specified on command line. */ +static char *fakehost = NULL; + +/* ... */ +#ifdef DEBUGGING +#define debug(s) fprintf(dbf,s); fflush(dbf) +#define DEBUGTERM "/dev/ttyp0" +FILE *dbf; +#else +#define debug(s) /* nothing */ +#endif + +int getty_main(int argc, char **argv) +{ + char *logname = NULL; /* login name, given to /bin/login */ + struct chardata chardata; /* set by get_logname() */ + struct termio termio; /* terminal mode bits */ + static struct options options = { + F_ISSUE, /* show /etc/issue (SYSV_STYLE) */ + 0, /* no timeout */ + _PATH_LOGIN, /* default login program */ + "tty1", /* default tty line */ + "", /* modem init string */ + ISSUE, /* default issue file */ + 0, /* no baud rates known yet */ + }; + +#ifdef DEBUGGING + dbf = xfopen(DEBUGTERM, "w"); + + { + int i; + + for (i = 1; i < argc; i++) { + debug(argv[i]); + debug("\n"); + } + } +#endif + + /* Parse command-line arguments. */ + + parse_args(argc, argv, &options); + +#ifdef __linux__ + setsid(); +#endif + + /* Update the utmp file. */ + +#ifdef SYSV_STYLE + update_utmp(options.tty); +#endif + + debug("calling open_tty\n"); + /* Open the tty as standard { input, output, error }. */ + open_tty(options.tty, &termio, options.flags & F_LOCAL); + +#ifdef __linux__ + { + int iv; + + iv = getpid(); + if (ioctl(0, TIOCSPGRP, &iv) < 0) + perror_msg("ioctl() TIOCSPGRP call failed"); + } +#endif + /* Initialize the termio settings (raw mode, eight-bit, blocking i/o). */ + debug("calling termio_init\n"); + termio_init(&termio, options.speeds[FIRST_SPEED], &options); + + /* write the modem init string and DON'T flush the buffers */ + if (options.flags & F_INITSTRING) { + debug("writing init string\n"); + write(1, options.initstring, strlen(options.initstring)); + } + + if (!(options.flags & F_LOCAL)) { + /* go to blocking write mode unless -L is specified */ + fcntl(1, F_SETFL, fcntl(1, F_GETFL, 0) & ~O_NONBLOCK); + } + + /* Optionally detect the baud rate from the modem status message. */ + debug("before autobaud\n"); + if (options.flags & F_PARSE) + auto_baud(&termio); + + /* Set the optional timer. */ + if (options.timeout) + (void) alarm((unsigned) options.timeout); + + /* optionally wait for CR or LF before writing /etc/issue */ + if (options.flags & F_WAITCRLF) { + char ch; + + debug("waiting for cr-lf\n"); + while (read(0, &ch, 1) == 1) { + ch &= 0x7f; /* strip "parity bit" */ +#ifdef DEBUGGING + fprintf(dbf, "read %c\n", ch); +#endif + if (ch == '\n' || ch == '\r') + break; + } + } + + chardata = init_chardata; + if (!(options.flags & F_NOPROMPT)) { + /* Read the login name. */ + debug("reading login name\n"); + /* while ((logname = get_logname(&options, &chardata, &termio)) == 0) */ + while ((logname = get_logname(&options, &chardata, &termio)) == + NULL) next_speed(&termio, &options); + } + + /* Disable timer. */ + + if (options.timeout) + (void) alarm(0); + + /* Finalize the termio settings. */ + + termio_final(&options, &termio, &chardata); + + /* Now the newline character should be properly written. */ + + (void) write(1, "\n", 1); + + /* Let the login program take care of password validation. */ + + (void) execl(options.login, options.login, "--", logname, (char *) 0); + error("%s: can't exec %s: %m", options.tty, options.login); + return (0); /* quiet GCC */ +} + +/* parse-args - parse command-line arguments */ + +static void parse_args(int argc, char **argv, struct options *op) +{ + extern char *optarg; /* getopt */ + extern int optind; /* getopt */ + int c; + + while (isascii(c = getopt(argc, argv, "I:LH:f:hil:mt:wn"))) { + switch (c) { + case 'I': + if (!(op->initstring = strdup(optarg))) { + error("can't malloc initstring"); + break; + } + { + char ch, *p, *q; + int i; + + /* copy optarg into op->initstring decoding \ddd + octal codes into chars */ + q = op->initstring; + p = optarg; + while (*p) { + if (*p == '\\') { /* know \\ means \ */ + p++; + if (*p == '\\') { + ch = '\\'; + p++; + } else { /* handle \000 - \177 */ + ch = 0; + for (i = 1; i <= 3; i++) { + if (*p >= '0' && *p <= '7') { + ch <<= 3; + ch += *p - '0'; + p++; + } else + break; + } + } + *q++ = ch; + } else { + *q++ = *p++; + } + } + *q = '\0'; + } + op->flags |= F_INITSTRING; + break; + + case 'L': /* force local */ + op->flags |= F_LOCAL; + break; + case 'H': /* fake login host */ + fakehost = optarg; + break; + case 'f': /* custom issue file */ + op->flags |= F_CUSTISSUE; + op->issue = optarg; + break; + case 'h': /* enable h/w flow control */ + op->flags |= F_RTSCTS; + break; + case 'i': /* do not show /etc/issue */ + op->flags &= ~F_ISSUE; + break; + case 'l': + op->login = optarg; /* non-default login program */ + break; + case 'm': /* parse modem status message */ + op->flags |= F_PARSE; + break; + case 'n': + op->flags |= F_NOPROMPT; + break; + case 't': /* time out */ + if ((op->timeout = atoi(optarg)) <= 0) + error("bad timeout value: %s", optarg); + break; + case 'w': + op->flags |= F_WAITCRLF; + break; + default: + show_usage(); + } + } + debug("after getopt loop\n"); + if (argc < optind + 2) /* check parameter count */ + show_usage(); + + /* we loosen up a bit and accept both "baudrate tty" and "tty baudrate" */ + if ('0' <= argv[optind][0] && argv[optind][0] <= '9') { + /* a number first, assume it's a speed (BSD style) */ + parse_speeds(op, argv[optind++]); /* baud rate(s) */ + op->tty = argv[optind]; /* tty name */ + } else { + op->tty = argv[optind++]; /* tty name */ + parse_speeds(op, argv[optind]); /* baud rate(s) */ + } + + optind++; + if (argc > optind && argv[optind]) + setenv("TERM", argv[optind], 1); + + debug("exiting parseargs\n"); +} + +/* parse_speeds - parse alternate baud rates */ + +static void parse_speeds(struct options *op, char *arg) +{ + char *cp; + + debug("entered parse_speeds\n"); + for (cp = strtok(arg, ","); cp != 0; cp = strtok((char *) 0, ",")) { + if ((op->speeds[op->numspeed++] = bcode(cp)) <= 0) + error("bad speed: %s", cp); + if (op->numspeed > MAX_SPEED) + error("too many alternate speeds"); + } + debug("exiting parsespeeds\n"); +} + +#ifdef SYSV_STYLE + +/* update_utmp - update our utmp entry */ +static void update_utmp(char *line) +{ + struct utmp ut; + struct utmp *utp; + time_t t; + int mypid = getpid(); +#if ! (__GLIBC__ > 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ >= 1)) + struct flock lock; +#endif + + /* + * The utmp file holds miscellaneous information about things started by + * /sbin/init and other system-related events. Our purpose is to update + * the utmp entry for the current process, in particular the process type + * and the tty line we are listening to. Return successfully only if the + * utmp file can be opened for update, and if we are able to find our + * entry in the utmp file. + */ + utmpname(_PATH_UTMP); + setutent(); + while ((utp = getutent()) + && !(utp->ut_type == INIT_PROCESS && utp->ut_pid == mypid)) /* nothing */ + ; + + if (utp) { + memcpy(&ut, utp, sizeof(ut)); + } else { + /* some inits don't initialize utmp... */ + memset(&ut, 0, sizeof(ut)); + strncpy(ut.ut_id, line + 3, sizeof(ut.ut_id)); + } + /*endutent(); */ + + strncpy(ut.ut_user, "LOGIN", sizeof(ut.ut_user)); + strncpy(ut.ut_line, line, sizeof(ut.ut_line)); + if (fakehost) + strncpy(ut.ut_host, fakehost, sizeof(ut.ut_host)); + time(&t); + ut.ut_time = t; + ut.ut_type = LOGIN_PROCESS; + ut.ut_pid = mypid; + + pututline(&ut); + endutent(); + + { + updwtmp(_PATH_WTMP, &ut); + } +} + +#endif + +/* open_tty - set up tty as standard { input, output, error } */ +static void open_tty(char *tty, struct termio *tp, int local) +{ + /* Get rid of the present standard { output, error} if any. */ + + (void) close(1); + (void) close(2); + errno = 0; /* ignore above errors */ + + /* Set up new standard input, unless we are given an already opened port. */ + + if (strcmp(tty, "-")) { + struct stat st; + + /* Sanity checks... */ + + if (chdir("/dev")) + error("/dev: chdir() failed: %m"); + if (stat(tty, &st) < 0) + error("/dev/%s: %m", tty); + if ((st.st_mode & S_IFMT) != S_IFCHR) + error("/dev/%s: not a character device", tty); + + /* Open the tty as standard input. */ + + (void) close(0); + errno = 0; /* ignore close(2) errors */ + + debug("open(2)\n"); + if (open(tty, O_RDWR | O_NONBLOCK, 0) != 0) + error("/dev/%s: cannot open as standard input: %m", tty); + + } else { + + /* + * Standard input should already be connected to an open port. Make + * sure it is open for read/write. + */ + + if ((fcntl(0, F_GETFL, 0) & O_RDWR) != O_RDWR) + error("%s: not open for read/write", tty); + } + + /* Set up standard output and standard error file descriptors. */ + debug("duping\n"); + if (dup(0) != 1 || dup(0) != 2) /* set up stdout and stderr */ + error("%s: dup problem: %m", tty); /* we have a problem */ + + /* + * The following ioctl will fail if stdin is not a tty, but also when + * there is noise on the modem control lines. In the latter case, the + * common course of action is (1) fix your cables (2) give the modem more + * time to properly reset after hanging up. SunOS users can achieve (2) + * by patching the SunOS kernel variable "zsadtrlow" to a larger value; + * 5 seconds seems to be a good value. + */ + + if (ioctl(0, TCGETA, tp) < 0) + error("%s: ioctl: %m", tty); + + /* + * It seems to be a terminal. Set proper protections and ownership. Mode + * 0622 is suitable for SYSV <4 because /bin/login does not change + * protections. SunOS 4 login will change the protections to 0620 (write + * access for group tty) after the login has succeeded. + */ + +#ifdef DEBIAN + { + /* tty to root.dialout 660 */ + struct group *gr; + int id; + + id = (gr = getgrnam("dialout")) ? gr->gr_gid : 0; + chown(tty, 0, id); + chmod(tty, 0660); + + /* vcs,vcsa to root.sys 600 */ + if (!strncmp(tty, "tty", 3) && isdigit(tty[3])) { + char *vcs, *vcsa; + + if (!(vcs = strdup(tty))) + error("Can't malloc for vcs"); + if (!(vcsa = malloc(strlen(tty) + 2))) + error("Can't malloc for vcsa"); + strcpy(vcs, "vcs"); + strcpy(vcs + 3, tty + 3); + strcpy(vcsa, "vcsa"); + strcpy(vcsa + 4, tty + 3); + + id = (gr = getgrnam("sys")) ? gr->gr_gid : 0; + chown(vcs, 0, id); + chmod(vcs, 0600); + chown(vcsa, 0, id); + chmod(vcs, 0600); + + free(vcs); + free(vcsa); + } + } +#else + (void) chown(tty, 0, 0); /* root, sys */ + (void) chmod(tty, 0622); /* crw--w--w- */ + errno = 0; /* ignore above errors */ +#endif +} + +/* termio_init - initialize termio settings */ + +static void termio_init(struct termio *tp, int speed, struct options *op) +{ + + /* + * Initial termio settings: 8-bit characters, raw-mode, blocking i/o. + * Special characters are set after we have read the login name; all + * reads will be done in raw mode anyway. Errors will be dealt with + * lateron. + */ +#ifdef __linux__ + /* flush input and output queues, important for modems! */ + (void) ioctl(0, TCFLSH, TCIOFLUSH); +#endif + + tp->c_cflag = CS8 | HUPCL | CREAD | speed; + if (op->flags & F_LOCAL) { + tp->c_cflag |= CLOCAL; + } + + tp->c_iflag = tp->c_lflag = tp->c_oflag = tp->c_line = 0; + tp->c_cc[VMIN] = 1; + tp->c_cc[VTIME] = 0; + + /* Optionally enable hardware flow control */ + +#ifdef CRTSCTS + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; +#endif + + (void) ioctl(0, TCSETA, tp); + + /* go to blocking input even in local mode */ + fcntl(0, F_SETFL, fcntl(0, F_GETFL, 0) & ~O_NONBLOCK); + + debug("term_io 2\n"); +} + +/* auto_baud - extract baud rate from modem status message */ +static void auto_baud(struct termio *tp) +{ + int speed; + int vmin; + unsigned iflag; + char buf[BUFSIZ]; + char *bp; + int nread; + + /* + * This works only if the modem produces its status code AFTER raising + * the DCD line, and if the computer is fast enough to set the proper + * baud rate before the message has gone by. We expect a message of the + * following format: + * + * + * + * The number is interpreted as the baud rate of the incoming call. If the + * modem does not tell us the baud rate within one second, we will keep + * using the current baud rate. It is advisable to enable BREAK + * processing (comma-separated list of baud rates) if the processing of + * modem status messages is enabled. + */ + + /* + * Use 7-bit characters, don't block if input queue is empty. Errors will + * be dealt with lateron. + */ + + iflag = tp->c_iflag; + tp->c_iflag |= ISTRIP; /* enable 8th-bit stripping */ + vmin = tp->c_cc[VMIN]; + tp->c_cc[VMIN] = 0; /* don't block if queue empty */ + (void) ioctl(0, TCSETA, tp); + + /* + * Wait for a while, then read everything the modem has said so far and + * try to extract the speed of the dial-in call. + */ + + (void) sleep(1); + if ((nread = read(0, buf, sizeof(buf) - 1)) > 0) { + buf[nread] = '\0'; + for (bp = buf; bp < buf + nread; bp++) { + if (isascii(*bp) && isdigit(*bp)) { + if ((speed = bcode(bp))) { + tp->c_cflag &= ~CBAUD; + tp->c_cflag |= speed; + } + break; + } + } + } + /* Restore terminal settings. Errors will be dealt with lateron. */ + + tp->c_iflag = iflag; + tp->c_cc[VMIN] = vmin; + (void) ioctl(0, TCSETA, tp); +} + +/* do_prompt - show login prompt, optionally preceded by /etc/issue contents */ +static void do_prompt(struct options *op, struct termio *tp) +{ +#ifdef ISSUE + FILE *fd; + int oflag; + int c; + struct utsname uts; + + (void) uname(&uts); +#endif + + (void) write(1, "\r\n", 2); /* start a new line */ +#ifdef ISSUE /* optional: show /etc/issue */ + if ((op->flags & F_ISSUE) && (fd = fopen(op->issue, "r"))) { + oflag = tp->c_oflag; /* save current setting */ + tp->c_oflag |= (ONLCR | OPOST); /* map NL in output to CR-NL */ + (void) ioctl(0, TCSETAW, tp); + + + while ((c = getc(fd)) != EOF) { + if (c == '\\') { + c = getc(fd); + + switch (c) { + case 's': + (void) printf("%s", uts.sysname); + break; + + case 'n': + (void) printf("%s", uts.nodename); + break; + + case 'r': + (void) printf("%s", uts.release); + break; + + case 'v': + (void) printf("%s", uts.version); + break; + + case 'm': + (void) printf("%s", uts.machine); + break; + + case 'o': + { + char domainname[256]; + + getdomainname(domainname, sizeof(domainname)); + domainname[sizeof(domainname) - 1] = '\0'; + printf("%s", domainname); + } + break; + + case 'd': + case 't': + { + char *weekday[] = { "Sun", "Mon", "Tue", "Wed", "Thu", + "Fri", "Sat" + }; + char *month[] = { "Jan", "Feb", "Mar", "Apr", "May", + "Jun", "Jul", "Aug", "Sep", "Oct", + "Nov", "Dec" + }; + time_t now; + struct tm *tm; + + (void) time(&now); + tm = localtime(&now); + + if (c == 'd') + (void) printf("%s %s %d %d", + weekday[tm->tm_wday], + month[tm->tm_mon], tm->tm_mday, + tm->tm_year < + 70 ? tm->tm_year + + 2000 : tm->tm_year + 1900); + else + (void) printf("%02d:%02d:%02d", tm->tm_hour, + tm->tm_min, tm->tm_sec); + + break; + } + + case 'l': + (void) printf("%s", op->tty); + break; + + case 'b': + { + int i; + + for (i = 0; speedtab[i].speed; i++) { + if (speedtab[i].code == (tp->c_cflag & CBAUD)) { + printf("%ld", speedtab[i].speed); + break; + } + } + break; + } + case 'u': + case 'U': + { + int users = 0; + struct utmp *ut; + + setutent(); + while ((ut = getutent())) + if (ut->ut_type == USER_PROCESS) + users++; + endutent(); + printf("%d ", users); + if (c == 'U') + printf((users == 1) ? "user" : "users"); + break; + } + default: + (void) putchar(c); + } + } else + (void) putchar(c); + } + fflush(stdout); + + tp->c_oflag = oflag; /* restore settings */ + (void) ioctl(0, TCSETAW, tp); /* wait till output is gone */ + (void) fclose(fd); + } +#endif +#ifdef __linux__ + { + char hn[MAXHOSTNAMELEN + 1]; + + (void) gethostname(hn, MAXHOSTNAMELEN); + write(1, hn, strlen(hn)); + } +#endif + (void) write(1, LOGIN, sizeof(LOGIN) - 1); /* always show login prompt */ +} + +/* next_speed - select next baud rate */ +static void next_speed(struct termio *tp, struct options *op) +{ + static int baud_index = FIRST_SPEED; /* current speed index */ + + baud_index = (baud_index + 1) % op->numspeed; + tp->c_cflag &= ~CBAUD; + tp->c_cflag |= op->speeds[baud_index]; + (void) ioctl(0, TCSETA, tp); +} + +/* get_logname - get user name, establish parity, speed, erase, kill, eol */ +/* return NULL on failure, logname on success */ +static char *get_logname(struct options *op, struct chardata *cp, struct termio *tp) +{ + static char logname[BUFSIZ]; + char *bp; + char c; /* input character, full eight bits */ + char ascval; /* low 7 bits of input character */ + int bits; /* # of "1" bits per character */ + int mask; /* mask with 1 bit up */ + static char *erase[] = { /* backspace-space-backspace */ + "\010\040\010", /* space parity */ + "\010\040\010", /* odd parity */ + "\210\240\210", /* even parity */ + "\210\240\210", /* no parity */ + }; + + /* Initialize kill, erase, parity etc. (also after switching speeds). */ + + *cp = init_chardata; + + /* Flush pending input (esp. after parsing or switching the baud rate). */ + + (void) sleep(1); + (void) ioctl(0, TCFLSH, TCIFLUSH); + + /* Prompt for and read a login name. */ + + for (*logname = 0; *logname == 0; /* void */ ) { + + /* Write issue file and prompt, with "parity" bit == 0. */ + + do_prompt(op, tp); + + /* Read name, watch for break, parity, erase, kill, end-of-line. */ + + for (bp = logname, cp->eol = 0; cp->eol == 0; /* void */ ) { + + /* Do not report trivial EINTR/EIO errors. */ + + if (read(0, &c, 1) < 1) { + if (errno == EINTR || errno == EIO) + exit(0); + error("%s: read: %m", op->tty); + } + /* Do BREAK handling elsewhere. */ + + if ((c == 0) && op->numspeed > 1) + /* return (0); */ + return NULL; + + /* Do parity bit handling. */ + + if (c != (ascval = (c & 0177))) { /* "parity" bit on ? */ + for (bits = 1, mask = 1; mask & 0177; mask <<= 1) + if (mask & ascval) + bits++; /* count "1" bits */ + cp->parity |= ((bits & 1) ? 1 : 2); + } + /* Do erase, kill and end-of-line processing. */ + + switch (ascval) { + case CR: + case NL: + *bp = 0; /* terminate logname */ + cp->eol = ascval; /* set end-of-line char */ + break; + case BS: + case DEL: + case '#': + cp->erase = ascval; /* set erase character */ + if (bp > logname) { + (void) write(1, erase[cp->parity], 3); + bp--; + } + break; + case CTL('U'): + case '@': + cp->kill = ascval; /* set kill character */ + while (bp > logname) { + (void) write(1, erase[cp->parity], 3); + bp--; + } + break; + case CTL('D'): + exit(0); + default: + if (!isascii(ascval) || !isprint(ascval)) { + /* ignore garbage characters */ ; + } else if (bp - logname >= sizeof(logname) - 1) { + error("%s: input overrun", op->tty); + } else { + (void) write(1, &c, 1); /* echo the character */ + *bp++ = ascval; /* and store it */ + } + break; + } + } + } + /* Handle names with upper case and no lower case. */ + + if ((cp->capslock = caps_lock(logname))) { + for (bp = logname; *bp; bp++) + if (isupper(*bp)) + *bp = tolower(*bp); /* map name to lower case */ + } + return (logname); +} + +/* termio_final - set the final tty mode bits */ +static void termio_final(struct options *op, struct termio *tp, struct chardata *cp) +{ + /* General terminal-independent stuff. */ + + tp->c_iflag |= IXON | IXOFF; /* 2-way flow control */ + tp->c_lflag |= ICANON | ISIG | ECHO | ECHOE | ECHOK | ECHOKE; + /* no longer| ECHOCTL | ECHOPRT */ + tp->c_oflag |= OPOST; + /* tp->c_cflag = 0; */ + tp->c_cc[VINTR] = DEF_INTR; /* default interrupt */ + tp->c_cc[VQUIT] = DEF_QUIT; /* default quit */ + tp->c_cc[VEOF] = DEF_EOF; /* default EOF character */ + tp->c_cc[VEOL] = DEF_EOL; +#ifdef __linux__ + tp->c_cc[VSWTC] = DEF_SWITCH; /* default switch character */ +#else + tp->c_cc[VSWTCH] = DEF_SWITCH; /* default switch character */ +#endif + + /* Account for special characters seen in input. */ + + if (cp->eol == CR) { + tp->c_iflag |= ICRNL; /* map CR in input to NL */ + tp->c_oflag |= ONLCR; /* map NL in output to CR-NL */ + } + tp->c_cc[VERASE] = cp->erase; /* set erase character */ + tp->c_cc[VKILL] = cp->kill; /* set kill character */ + + /* Account for the presence or absence of parity bits in input. */ + + switch (cp->parity) { + case 0: /* space (always 0) parity */ + break; + case 1: /* odd parity */ + tp->c_cflag |= PARODD; + /* FALLTHROUGH */ + case 2: /* even parity */ + tp->c_cflag |= PARENB; + tp->c_iflag |= INPCK | ISTRIP; + /* FALLTHROUGH */ + case (1 | 2): /* no parity bit */ + tp->c_cflag &= ~CSIZE; + tp->c_cflag |= CS7; + break; + } + /* Account for upper case without lower case. */ + + if (cp->capslock) { + tp->c_iflag |= IUCLC; + tp->c_lflag |= XCASE; + tp->c_oflag |= OLCUC; + } + /* Optionally enable hardware flow control */ + +#ifdef CRTSCTS + if (op->flags & F_RTSCTS) + tp->c_cflag |= CRTSCTS; +#endif + + /* Finally, make the new settings effective */ + + if (ioctl(0, TCSETA, tp) < 0) + error("%s: ioctl: TCSETA: %m", op->tty); +} + +/* caps_lock - string contains upper case without lower case */ +/* returns 1 if true, 0 if false */ +static int caps_lock(const char *s) +{ + int capslock; + + for (capslock = 0; *s; s++) { + if (islower(*s)) + return (0); + if (capslock == 0) + capslock = isupper(*s); + } + return (capslock); +} + +/* bcode - convert speed string to speed code; return 0 on failure */ +static int bcode(const char *s) +{ + struct Speedtab *sp; + long speed = atol(s); + + for (sp = speedtab; sp->speed; sp++) + if (sp->speed == speed) + return (sp->code); + return (0); +} + +/* error - report errors to console or syslog; only understands %s and %m */ + +#define str2cpy(b,s1,s2) strcat(strcpy(b,s1),s2) + +/* + * output error messages + */ +static void error(const char *fmt, ...) +{ + va_list va_alist; + char buf[256], *bp; + +#ifndef USE_SYSLOG + int fd; +#endif + +#ifdef USE_SYSLOG + buf[0] = '\0'; + bp = buf; +#else + strncpy(buf, applet_name, 256); + strncat(buf, ": ", 256); + buf[255] = 0; + bp = buf + strlen(buf); +#endif + + va_start(va_alist, fmt); + vsnprintf(bp, 256 - strlen(buf), fmt, va_alist); + buf[255] = 0; + va_end(va_alist); + +#ifdef USE_SYSLOG + syslog_msg(LOG_AUTH, LOG_ERR, buf); +#else + strncat(bp, "\r\n", 256 - strlen(buf)); + buf[255] = 0; + if ((fd = open("/dev/console", 1)) >= 0) { + write(fd, buf, strlen(buf)); + close(fd); + } +#endif + (void) sleep((unsigned) 10); /* be kind to init(8) */ + exit(1); +} diff --git a/loginutils/login.c b/loginutils/login.c new file mode 100644 index 000000000..4d93ece49 --- /dev/null +++ b/loginutils/login.c @@ -0,0 +1,459 @@ +/* vi: set sw=4 ts=4: */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "busybox.h" + +#include "pwd.h" +#include "grp.h" + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS +#include "shadow.h" +#endif + +#include "tinylogin.h" + +// import from utmp.c +static void checkutmp(int picky); +static void setutmp(const char *name, const char *line); + +// import from encrypt.c +extern char *pw_encrypt(const char *clear, const char *salt); + + +// login defines +#define DEFAULT_USER "UNKNOWN" +#define DEFAULT_PWD "!" +#define DEFAULT_SHELL "/bin/sh" +#define TIMEOUT 60 +#define FAIL_DELAY 3 +#define MOTD_FILE "/etc/motd" +#define NOLOGIN_FILE "/etc/nologin" +#define SECURETTY_FILE "/etc/securetty" + +/* Stuff global to this file */ +struct utmp utent; + + +static int check_nologin ( int amroot ); + +#if defined CONFIG_FEATURE_SECURETTY +static int check_tty ( const char *tty ); + +#else +static inline int check_tty ( const char *tty ) { return 1; } + +#endif + +static int is_my_tty ( const char *tty ); +static const char *login_prompt ( void ); +static void motd ( void ); +static void set_env(int argc, char *const *argv); + + +static void alarm_handler ( int sig ) +{ + error_msg ( "\nLogin timed out after %d seconds.\n", TIMEOUT ); + exit ( EXIT_SUCCESS ); +} + + +extern int login_main(int argc, char **argv) +{ + char tty[BUFSIZ]; + char full_tty[200]; + char fromhost[512]; + char *tmp; + int amroot; + int flag; + int failed; + int count=0; + struct passwd *pw, pw_copy; + + int opt_preserve = 0; + int opt_fflag = 0; + char *opt_host = 0; + const char *username = 0; + int alarmstarted = 0; + + amroot = ( getuid ( ) == 0 ); + signal ( SIGALRM, alarm_handler ); + + if (( argc > 1 ) && ( TIMEOUT > 0 )) { + alarm ( TIMEOUT ); + alarmstarted = 1; + } + + while (( flag = getopt(argc, argv, "f:h:p")) != EOF ) { + switch ( flag ) { + case 'p': + printf ( "PRESERVE\n" ); + opt_preserve = 1; + break; + case 'f': + /* + * username must be a seperate token + * (-f root, *NOT* -froot). --marekm + */ + if ( optarg != argv[optind-1] ) + show_usage ( ); + + if ( !amroot ) /* Auth bypass only if real UID is zero */ + error_msg_and_die ( "login: -f permission denied\n" ); + + printf ( "USERNAME: %s\n", optarg ); + + username = optarg; + opt_fflag = 1; + break; + case 'h': + opt_host = optarg; + break; + default: + show_usage ( ); + } + } + + if ( optind < argc ) // got a username + username = argv [optind++]; + + if ( !isatty ( 0 ) || !isatty ( 1 ) || !isatty ( 2 )) + return EXIT_FAILURE; /* Must be a terminal */ + + checkutmp ( !amroot ); + + tmp = ttyname ( 0 ); + if ( tmp && ( strncmp ( tmp, "/dev/", 5 ) == 0 )) + safe_strncpy ( tty, tmp + 5, sizeof( tty )); + else + safe_strncpy ( tty, "UNKNOWN", sizeof( tty )); + + if ( amroot ) + memset ( utent.ut_host, 0, sizeof utent.ut_host ); + + if ( opt_host ) { + safe_strncpy ( utent.ut_host, opt_host, sizeof( utent. ut_host )); + + snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s' from `%.200s'", tty, opt_host ); + } + else + snprintf ( fromhost, sizeof( fromhost ) - 1, " on `%.100s'", tty ); + + openlog ( "login", LOG_PID | LOG_CONS | LOG_NOWAIT, LOG_AUTH ); + + while ( 1 ) { + failed = 0; + + if ( !username || !username[0] ) + username = login_prompt ( ); + + if ( !alarmstarted && ( TIMEOUT > 0 )) { + alarm ( TIMEOUT ); + alarmstarted = 1; + } + + if (!( pw = getpwnam ( username ))) { + pw_copy. pw_name = DEFAULT_USER; + pw_copy. pw_passwd = DEFAULT_PWD; + pw_copy. pw_shell = DEFAULT_SHELL; + opt_fflag = 0; + failed = 1; + } else + pw_copy = *pw; + + pw = &pw_copy; + + if (( pw-> pw_passwd [0] == '!' ) || ( pw-> pw_passwd[0] == '*' )) + failed = 1; + + if ( opt_fflag ) { + opt_fflag = 0; + goto auth_ok; + } + + if (( pw-> pw_uid == 0 ) && ( !check_tty ( tty ))) + failed = 1; + + /* Don't check the password if password entry is empty (!) */ + if ( !pw-> pw_passwd[0] ) + goto auth_ok; + + /* authorization takes place here */ + if ( correct_password ( pw )) + goto auth_ok; + + syslog ( LOG_WARNING, "invalid password for `%s'%s\n", pw-> pw_name, fromhost); + failed = 1; + +auth_ok: + if ( !failed) + break; + + { // delay next try + time_t start, now; + + time ( &start ); + now = start; + while ( difftime ( now, start ) < FAIL_DELAY) { + sleep ( FAIL_DELAY ); + time ( &now ); + } + } + + puts("Login incorrect"); + if ( ++count == 3 ) + return EXIT_FAILURE; + } + + alarm ( 0 ); + if ( check_nologin ( pw-> pw_uid == 0 )) + return EXIT_FAILURE; + + setutmp ( username, tty ); + if ( *tty != '/' ) + snprintf ( full_tty, sizeof( full_tty ) - 1, "/dev/%s", tty); + else + safe_strncpy ( full_tty, tty, sizeof( full_tty ) - 1 ); + + if ( !is_my_tty ( full_tty )) + syslog ( LOG_ERR, "unable to determine TTY name, got %s\n", full_tty ); + + /* Try these, but don't complain if they fail + * (for example when the root fs is read only) */ + chown ( full_tty, pw-> pw_uid, pw-> pw_gid ); + chmod ( full_tty, 0600 ); + + change_identity ( pw ); + setup_environment ( pw-> pw_shell, 1, !opt_preserve, pw ); + + motd ( ); + signal ( SIGALRM, SIG_DFL ); /* default alarm signal */ + + if ( pw-> pw_uid == 0 ) + syslog ( LOG_INFO, "root login %s\n", fromhost ); + + run_shell ( pw-> pw_shell, 1, 0, 0 ); /* exec the shell finally. */ + + return EXIT_FAILURE; +} + + + +static const char *login_prompt ( void ) +{ + char buf [1024]; + char *sp, *ep; + + gethostname ( buf, sizeof( buf )); + printf ( "\nBusyBox on %s login: ", buf ); + fflush ( stdout ); + + if ( !fgets ( buf, sizeof( buf ) - 1, stdin )) + return 0; + + if ( !strchr ( buf, '\n' )); + return 0; + + for ( sp = buf; isspace ( *sp ); sp++ ) { } + for ( ep = sp; isgraph ( *ep ); ep++ ) { } + + *ep = 0; + return sp; +} + + +static int check_nologin ( int amroot ) +{ + if ( access ( NOLOGIN_FILE, F_OK ) == 0 ) { + FILE *fp; + int c; + + if (( fp = fopen ( NOLOGIN_FILE, "r" ))) { + while (( c = getc ( fp )) != EOF ) + putchar (( c == '\n' ) ? '\r' : c ); + + fflush ( stdout ); + fclose ( fp ); + } else { + puts ( "\r\nSystem closed for routine maintenance.\r" ); + } + if ( !amroot ) + return 1; + + puts ( "\r\n[Disconnect bypassed -- root login allowed.]\r" ); + } + return 0; +} + +#ifdef CONFIG_FEATURE_SECURETTY + +static int check_tty ( const char *tty ) +{ + FILE *fp; + int i; + char buf[BUFSIZ]; + + if (( fp = fopen ( SECURETTY_FILE, "r" ))) { + while ( fgets ( buf, sizeof( buf ) - 1, fp )) { + for ( i = xstrlen( buf ) - 1; i >= 0; --i ) { + if ( !isspace ( buf[i] )) + break; + } + buf[++i] = '\0'; + if (( buf [0] == '\0' ) || ( buf [0] == '#' )) + continue; + + if ( strcmp ( buf, tty ) == 0 ) { + fclose ( fp ); + return 1; + } + } + fclose(fp); + return 0; + } + else { + syslog ( LOG_WARNING, "cannot open securetty file.\n" ); + return 1; + } +} + +#endif + +/* returns 1 if true */ +static int is_my_tty ( const char *tty ) +{ + struct stat by_name, by_fd; + + if ( stat ( tty, &by_name ) || fstat ( 0, &by_fd )) + return 0; + + if ( by_name. st_rdev != by_fd. st_rdev ) + return 0; + else + return 1; +} + + +static void motd ( ) +{ + FILE *fp; + register int c; + + if (( fp = fopen ( MOTD_FILE, "r" ))) { + while (( c = getc ( fp )) != EOF ) + putchar ( c ); + fclose ( fp ); + } +} + + +// vv Taken from tinylogin utmp.c vv + +#define _WTMP_FILE "/var/log/wtmp" + +#define NO_UTENT \ + "No utmp entry. You must exec \"login\" from the lowest level \"sh\"" +#define NO_TTY \ + "Unable to determine your tty name." + +/* + * checkutmp - see if utmp file is correct for this process + * + * System V is very picky about the contents of the utmp file + * and requires that a slot for the current process exist. + * The utmp file is scanned for an entry with the same process + * ID. If no entry exists the process exits with a message. + * + * The "picky" flag is for network and other logins that may + * use special flags. It allows the pid checks to be overridden. + * This means that getty should never invoke login with any + * command line flags. + */ + +static void checkutmp(int picky) +{ + char *line; + struct utmp *ut; + pid_t pid = getpid(); + + setutent(); + + /* First, try to find a valid utmp entry for this process. */ + while ((ut = getutent())) + if (ut->ut_pid == pid && ut->ut_line[0] && ut->ut_id[0] && + (ut->ut_type == LOGIN_PROCESS || ut->ut_type == USER_PROCESS)) + break; + + /* If there is one, just use it, otherwise create a new one. */ + if (ut) { + utent = *ut; + } else { + if (picky) { + puts(NO_UTENT); + exit(1); + } + line = ttyname(0); + if (!line) { + puts(NO_TTY); + exit(1); + } + if (strncmp(line, "/dev/", 5) == 0) + line += 5; + memset((void *) &utent, 0, sizeof utent); + utent.ut_type = LOGIN_PROCESS; + utent.ut_pid = pid; + strncpy(utent.ut_line, line, sizeof utent.ut_line); + /* XXX - assumes /dev/tty?? */ + strncpy(utent.ut_id, utent.ut_line + 3, sizeof utent.ut_id); + strncpy(utent.ut_user, "LOGIN", sizeof utent.ut_user); + time(&utent.ut_time); + } +} + +#if __GNU_LIBRARY__ < 5 +/* + * Some systems already have updwtmp() and possibly updwtmpx(). Others + * don't, so we re-implement these functions if necessary. --marekm + */ +static void updwtmp(const char *filename, const struct utmp *ut) +{ + int fd; + + fd = open(filename, O_APPEND | O_WRONLY, 0); + if (fd >= 0) { + write(fd, (const char *) ut, sizeof(*ut)); + close(fd); + } +} +#endif + +/* + * setutmp - put a USER_PROCESS entry in the utmp file + * + * setutmp changes the type of the current utmp entry to + * USER_PROCESS. the wtmp file will be updated as well. + */ + +static void setutmp(const char *name, const char *line) +{ + utent.ut_type = USER_PROCESS; + strncpy(utent.ut_user, name, sizeof utent.ut_user); + time(&utent.ut_time); + /* other fields already filled in by checkutmp above */ + setutent(); + pututline(&utent); + endutent(); + updwtmp(_WTMP_FILE, &utent); +} 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 @@ +/* vi: set sw=4 ts=4: */ + +#include "busybox.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "pwd.h" +#include "grp.h" + +#include "tinylogin.h" + + + +/* The shell to run if none is given in the user's passwd entry. */ +#define DEFAULT_SHELL "/bin/sh" +#define DEFAULT_USER "root" + +//#define SYSLOG_SUCCESS +#define SYSLOG_FAILURE + + +#if defined( SYSLOG_SUCCESS ) || defined( SYSLOG_FAILURE ) +/* Log the fact that someone has run su to the user given by PW; + if SUCCESSFUL is nonzero, they gave the correct password, etc. */ + +static void log_su ( const struct passwd *pw, int successful ) +{ + const char *old_user, *tty; + +#if !defined( SYSLOG_SUCESS ) + if ( successful ) + return; +#endif +#if !defined( SYSLOG_FAILURE ) + if ( !successful ) + return; +#endif + + if ( pw-> pw_uid ) // not to root -> ignored + return; + + /* The utmp entry (via getlogin) is probably the best way to identify + the user, especially if someone su's from a su-shell. */ + old_user = getlogin ( ); + if ( !old_user ) { + /* getlogin can fail -- usually due to lack of utmp entry. Resort to getpwuid. */ + struct passwd *pwd = getpwuid ( getuid ( )); + old_user = ( pwd ? pwd-> pw_name : "" ); + } + + tty = ttyname ( 2 ); + + openlog ( "su", 0, LOG_AUTH ); + syslog ( LOG_NOTICE, "%s%s on %s", successful ? "" : "FAILED SU ", old_user, tty ? tty : "none" ); +} +#endif + + + +int su_main ( int argc, char **argv ) +{ + int flag; + int opt_preserve = 0; + int opt_loginshell = 0; + char *opt_shell = 0; + char *opt_command = 0; + char *opt_username = DEFAULT_USER; + char **opt_args = 0; + struct passwd *pw, pw_copy; + + + while (( flag = getopt ( argc, argv, "c:lmps:" )) != -1 ) { + switch ( flag ) { + case 'c': + opt_command = optarg; + break; + case 'm': + case 'p': + opt_preserve = 1; + break; + case 's': + opt_shell = optarg; + break; + case 'l': + opt_loginshell = 1; + break; + default: + show_usage ( ); + break; + } + } + + if (( optind < argc ) && ( argv [optind][0] == '-' ) && ( argv [optind][1] == 0 )) { + opt_loginshell = 1; + ++optind; + } + + /* get user if specified */ + if ( optind < argc ) + opt_username = argv [optind++]; + + if ( optind < argc ) + opt_args = argv + optind; + + + pw = getpwnam ( opt_username ); + if ( !pw ) + error_msg_and_die ( "user %s does not exist", opt_username ); + + /* Make sure pw->pw_shell is non-NULL. It may be NULL when NEW_USER + is a username that is retrieved via NIS (YP), but that doesn't have + a default shell listed. */ + if ( !pw-> pw_shell || !pw->pw_shell [0] ) + pw-> pw_shell = (char *) DEFAULT_SHELL; + + /* Make a copy of the password information and point pw at the local + copy instead. Otherwise, some systems (e.g. Linux) would clobber + the static data through the getlogin call from log_su. */ + pw_copy = *pw; + pw = &pw_copy; + pw-> pw_name = xstrdup ( pw-> pw_name ); + pw-> pw_dir = xstrdup ( pw-> pw_dir ); + pw-> pw_shell = xstrdup ( pw-> pw_shell ); + + if (( getuid ( ) == 0 ) || correct_password ( pw )) + log_su ( pw, 1 ); + else { + log_su ( pw, 0 ); + error_msg_and_die ( "incorrect password" ); + } + + if ( !opt_shell && opt_preserve ) + opt_shell = getenv ( "SHELL" ); + + if ( opt_shell && getuid ( ) && restricted_shell ( pw-> pw_shell )) + { + /* The user being su'd to has a nonstandard shell, and so is + probably a uucp account or has restricted access. Don't + compromise the account by allowing access with a standard + shell. */ + fputs ( "using restricted shell\n", stderr ); + opt_shell = 0; + } + + if ( !opt_shell ) + opt_shell = xstrdup ( pw-> pw_shell ); + + change_identity ( pw ); + setup_environment ( opt_shell, opt_loginshell, !opt_preserve, pw ); + run_shell ( opt_shell, opt_loginshell, opt_command, opt_args ); + + return EXIT_FAILURE; +} 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 @@ +/* vi: set sw=4 ts=4: */ +/* + * Copyright 1989 - 1991, Julianne Frances Haugh + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. Neither the name of Julianne F. Haugh nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY JULIE HAUGH AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL JULIE HAUGH OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include "busybox.h" + +#include +#include +#include +#include +#include +#include +#include + +#include "pwd.h" +#include "grp.h" + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS +#include "shadow.h" +#endif + +#define DEFAULT_LOGIN_PATH "/bin:/usr/bin" +#define DEFAULT_ROOT_LOGIN_PATH "/usr/sbin:/bin:/usr/bin:/sbin" + + +static void xsetenv ( const char *key, const char *value ) +{ + if ( setenv ( key, value, 1 )) + error_msg_and_die ( "out of memory" ); +} + +/* Become the user and group(s) specified by PW. */ + +void change_identity ( const struct passwd *pw ) +{ + if ( initgroups ( pw-> pw_name, pw-> pw_gid ) == -1 ) + perror_msg_and_die ( "cannot set groups" ); + endgrent ( ); + + if ( setgid ( pw-> pw_gid )) + perror_msg_and_die ( "cannot set group id" ); + if ( setuid ( pw->pw_uid )) + perror_msg_and_die ( "cannot set user id" ); +} + +/* Run SHELL, or DEFAULT_SHELL if SHELL is empty. + If COMMAND is nonzero, pass it to the shell with the -c option. + If ADDITIONAL_ARGS is nonzero, pass it to the shell as more + arguments. */ + +void run_shell ( const char *shell, int loginshell, const char *command, const char **additional_args ) +{ + const char **args; + int argno = 1; + int additional_args_cnt = 0; + + for ( args = additional_args; args && *args; args++ ) + additional_args_cnt++; + + if ( additional_args ) + args = (const char **) xmalloc (sizeof (char *) * ( 4 + additional_args_cnt )); + else + args = (const char **) xmalloc (sizeof (char *) * 4 ); + + args [0] = get_last_path_component ( xstrdup ( shell )); + + if ( loginshell ) { + char *args0 = xmalloc ( xstrlen ( args [0] ) + 2 ); + args0 [0] = '-'; + strcpy ( args0 + 1, args [0] ); + args [0] = args0; + } + + if ( command ) { + args [argno++] = "-c"; + args [argno++] = command; + } + if ( additional_args ) { + for ( ; *additional_args; ++additional_args ) + args [argno++] = *additional_args; + } + args [argno] = 0; + execv ( shell, (char **) args ); + perror_msg_and_die ( "cannot run %s", shell ); +} + +/* Return 1 if SHELL is a restricted shell (one not returned by + getusershell), else 0, meaning it is a standard shell. */ + +int restricted_shell ( const char *shell ) +{ + char *line; + + setusershell ( ); + while (( line = getusershell ( ))) { + if (( *line != '#' ) && ( strcmp ( line, shell ) == 0 )) + break; + } + endusershell ( ); + return line ? 0 : 1; +} + +/* Update `environ' for the new shell based on PW, with SHELL being + the value for the SHELL environment variable. */ + +void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ) +{ + if ( loginshell ) { + char *term; + + /* Change the current working directory to be the home directory + * of the user. It is a fatal error for this process to be unable + * to change to that directory. There is no "default" home + * directory. + * Some systems default to HOME=/ + */ + if ( chdir ( pw-> pw_dir )) { + if ( chdir ( "/" )) { + syslog ( LOG_WARNING, "unable to cd to %s' for user %s'\n", pw-> pw_dir, pw-> pw_name ); + error_msg_and_die ( "cannot cd to home directory or /" ); + } + fputs ( "warning: cannot change to home directory\n", stderr ); + } + + /* Leave TERM unchanged. Set HOME, SHELL, USER, LOGNAME, PATH. + Unset all other environment variables. */ + term = getenv ("TERM"); + clearenv ( ); + if ( term ) + xsetenv ( "TERM", term ); + xsetenv ( "HOME", pw-> pw_dir ); + xsetenv ( "SHELL", shell ); + xsetenv ( "USER", pw-> pw_name ); + xsetenv ( "LOGNAME", pw-> pw_name ); + xsetenv ( "PATH", ( pw-> pw_uid ? DEFAULT_LOGIN_PATH : DEFAULT_ROOT_LOGIN_PATH )); + } + else if ( changeenv ) { + /* Set HOME, SHELL, and if not becoming a super-user, + USER and LOGNAME. */ + xsetenv ( "HOME", pw-> pw_dir ); + xsetenv ( "SHELL", shell ); + if ( pw-> pw_uid ) { + xsetenv ( "USER", pw-> pw_name ); + xsetenv ( "LOGNAME", pw-> pw_name ); + } + } +} + +/* Ask the user for a password. + Return 1 if the user gives the correct password for entry PW, + 0 if not. Return 1 without asking for a password if run by UID 0 + or if PW has an empty password. */ + +int correct_password ( const struct passwd *pw ) +{ + char *unencrypted, *encrypted, *correct; + +#ifdef CONFIG_FEATURE_SHADOWPASSWDS + if (( strcmp ( pw-> pw_passwd, "x" ) == 0 ) || ( strcmp ( pw-> pw_passwd, "*" ) == 0 )) { + struct spwd *sp = getspnam ( pw-> pw_name ); + + if ( !sp ) + error_msg_and_die ( "no valid shadow password" ); + + correct = sp-> sp_pwdp; + } + else +#endif + correct = pw-> pw_passwd; + + if ( correct == 0 || correct[0] == '\0' ) + return 1; + + unencrypted = getpass ( "Password: " ); + if ( !unencrypted ) + { + fputs ( "getpass: cannot open /dev/tty\n", stderr ); + return 0; + } + encrypted = crypt ( unencrypted, correct ); + memset ( unencrypted, 0, xstrlen ( unencrypted )); + return ( strcmp ( encrypted, correct ) == 0 ) ? 1 : 0; +} 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 @@ +#ifndef BB_LOGINUTILS_SHELL_H +#define BB_LOGINUTILS_SHELL_H + +extern void change_identity ( const struct passwd *pw ); +extern void run_shell ( const char *shell, int loginshell, const char *command, char **additional_args ); +extern int restricted_shell ( const char *shell ); +extern void setup_environment ( const char *shell, int loginshell, int changeenv, const struct passwd *pw ); +extern int correct_password ( const struct passwd *pw ); + +#endif -- cgit v1.2.3