diff options
author | Dmitry Bogatov <KAction@gnu.org> | 2017-09-01 04:09:35 +0300 |
---|---|---|
committer | Dmitry Bogatov <KAction@gnu.org> | 2018-02-14 19:10:28 +0300 |
commit | d409f1899d83788d23ee2de03aef58d8e870b033 (patch) | |
tree | 72bba1af4427b58ede705943133e3d13fedad206 | |
parent | f8621b4aafe28a13c42de31b094f2763b1c3409f (diff) |
New upstream version 0.7
-rw-r--r-- | .cvsignore | 4 | ||||
-rw-r--r-- | CHANGES | 54 | ||||
-rw-r--r-- | COPYING | 340 | ||||
-rw-r--r-- | Makefile | 59 | ||||
-rw-r--r-- | README | 51 | ||||
-rw-r--r-- | README.PAM | 28 | ||||
-rw-r--r-- | checkpassword-pam.c | 74 | ||||
-rw-r--r-- | checkpassword-pam2.c | 131 | ||||
-rw-r--r-- | checkpassword.c | 91 | ||||
-rw-r--r-- | fgetty.8 | 90 | ||||
-rw-r--r-- | fgetty.c | 319 | ||||
-rw-r--r-- | fmt.h | 25 | ||||
-rw-r--r-- | fmt_ulong.c | 13 | ||||
-rw-r--r-- | login.c | 149 | ||||
-rw-r--r-- | login2.c | 110 |
15 files changed, 1538 insertions, 0 deletions
diff --git a/.cvsignore b/.cvsignore new file mode 100644 index 0000000..6d9d856 --- /dev/null +++ b/.cvsignore @@ -0,0 +1,4 @@ +checkpassword +fgetty +login +login2 @@ -0,0 +1,54 @@ +0.7: + You can now run fgetty as "fgetty 1" and it will try "/dev/vc/1" and + "/dev/tty1" before giving up. That means, the same fgetty line can + be run with and without devfs. Your boot sequence may fail but at + least you get a getty! + call setsid in case your init does not do it. Minit and sysvinit do. + Now you can run fgetty under runit, and maybe even directly from + the console. Patch by John Palkovic. + checkpassword now gives an error message if stdout is a TTY. + fgetty now works (a little) if it can't set the controlling TTY. + checkpassword now also accepts an empty password if the password + field in /etc/passwd or /etc/shadow is empty. + fix bug if someone left descriptors open in login.c (Florian Westphal) + +0.6: + the utmp code was broken. It only looked for the PID, not for the + "line" (device). So utmp grew needlessly. + fixed make install + add man page from Tino Reichardt + Enrico Scholz sent a patch that adds --long-hostname and adds error + checking for gethostname when it is not \0-terminated. + +0.5 + login will accept passwords up to 99 characters (previously it limited + to 8 because DES crypt() only looks at the first 8 characters anyway). + With MD5 crypt support, that's not so smart a move. Laurent BERCOT + reported this bug. Thanks, Laurent! + + Also, login will try to add users to group "console". This can be + used to give console users write access to the sound device. + +0.4 + fgetty will turn echo off before executing login. This fixes the + age-old security problem when the load is heavy at login: + + host login: hax0r + imsol33tPassword: _ + +0.3.1 + login2 contained a bad beginner's error regarding error checking when + reading motd. If you didn't have motd, login2 would loop :-( + Found and fixed by Michael Bacarella. + +0.3 + added a checkpassword that accepts a "nosetuid" flag in the + additional data section of the checkpassword API and then omits the + setuid(). Then, the called application (in the fgetty case, + /bin/login2) can do a "chown $UID. $TTY" before doing the setuid() + call itself, so finally, your TTY belongs to you. screen was always + complaining ;) + +0.2: + added login and login2 and edited README to "document" them. + fgetty will now also put $TTY and $HOST in the environment. @@ -0,0 +1,340 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Library General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Programs + + If you develop a new program, and you want it to be of the greatest +possible use to the public, the best way to achieve this is to make it +free software which everyone can redistribute and change under these terms. + + To do so, attach the following notices to the program. It is safest +to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least +the "copyright" line and a pointer to where the full notice is found. + + <one line to give the program's name and a brief idea of what it does.> + Copyright (C) <year> <name of author> + + 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 + + +Also add information on how to contact you by electronic and paper mail. + +If the program is interactive, make it output a short notice like this +when it starts in an interactive mode: + + Gnomovision version 69, Copyright (C) year name of author + Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. + This is free software, and you are welcome to redistribute it + under certain conditions; type `show c' for details. + +The hypothetical commands `show w' and `show c' should show the appropriate +parts of the General Public License. Of course, the commands you use may +be called something other than `show w' and `show c'; they could even be +mouse-clicks or menu items--whatever suits your program. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the program, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the program + `Gnomovision' (which makes passes at compilers) written by James Hacker. + + <signature of Ty Coon>, 1 April 1989 + Ty Coon, President of Vice + +This General Public License does not permit incorporating your program into +proprietary programs. If your program is a subroutine library, you may +consider it more useful to permit linking proprietary applications with the +library. If this is what you want to do, use the GNU Library General +Public License instead of this License. diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..730a176 --- /dev/null +++ b/Makefile @@ -0,0 +1,59 @@ +# Uncomment the following if you are a distribution maker and want to +# install to somewhere else than / +#DESTDIR=/tmp/fefix + +all: fgetty login login2 checkpassword + +DIET=diet -Os +#CROSS=arm-linux- +CROSS= +LDFLAGS=-s + +%.o: %.c +# gcc -march=i386 -mcpu=i386 -pipe -Os -fomit-frame-pointer -I../dietlibc/include -c $^ -DTEST + $(DIET) $(CROSS)$(CC) -pipe -Os -fomit-frame-pointer -I../dietlibc/include -c $^ -DTEST +# gcc -march=i386 -mcpu=i386 -pipe -g -I../dietlibc/include -DTEST -c $^ + $(CROSS)strip -x -R .comment -R .note $@ + +%: %.o + $(DIET) $(CROSS)$(CC) -o $@ $^ $(LDFLAGS) + +fgetty: fgetty.o fmt_ulong.o + +login: login.o +login2: login2.o +checkpassword: checkpassword.o +checkpassword-pam: checkpassword-pam.o checkpassword-pam2.o + $(CROSS)$(CC) -o $@ $^ -lmisc $(LDFLAGS) + +debug: fgetty.c fmt_ulong.o + gcc -g -o debug fgetty.c fmt_ulong.o -DDEBUG + +install: + install -d $(DESTDIR)/bin $(DESTDIR)/sbin $(DESTDIR)/usr/man/man8 + install login $(DESTDIR)/bin/login1 + install login2 $(DESTDIR)/bin + install fgetty $(DESTDIR)/sbin + install checkpassword $(DESTDIR)/bin/checkpassword.login + install -m 644 fgetty.8 $(DESTDIR)/usr/man/man8/fgetty.8 + @echo "now change your /etc/inittab to do something like" + @echo " 1:123:respawn:/sbin/fgetty /dev/vc/1 --noclear" + +clean: + rm -f *.o fgetty debug dietgetty login login2 checkpassword core + +sigs: fgetty.sig login.sig login2.sig checkpassword.sig + +.SUFFIXES: .sig +%.sig: % + gpg --detach-sign $< + +VERSION=fgetty-$(shell head -n 1 CHANGES|sed 's/://') +CURNAME=$(notdir $(shell pwd)) + +tar: clean rename + cd ..; tar cvvf $(VERSION).tar.bz2 $(VERSION) --use=bzip2 --exclude CVS + +rename: + if test $(CURNAME) != $(VERSION); then cd .. && mv $(CURNAME) $(VERSION); fi + @@ -0,0 +1,51 @@ + +This is actually a mingetty without the printfs. +Why? Because then you can link it against dietlibc +(http://www.fefe.de/dietlibc/). + +Actually, diet libc now supports printf, but not using it makes binaries +smaller nonetheless. + +The difference is remarkable: + +USER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMAND +root 5487 0.0 0.1 1180 444 tty5 S 00:40 0:00 /sbin/mingetty tty5 +root 6035 0.0 0.0 16 16 tty5 S 00:45 0:00 /sbin/fgetty tty5 + + + +I saw that on some really absurdly broken crap Linux distributions +(apparently the ones using PAM ;-}), login stay in memory as long as a +user is logged in, wasting precious memory. So I decided to write a +replacement login as well. + +My login uses the checkpassword (see http://cr.yp.to/checkpwd.html) +password checking interface. For the casual end-user, this means that +you will need to get and install checkpassword. For others, this means +unprecedented flexibility for the system administrator, because the +authentication for login can be done separately from login, and without +sacrificing system resources. + +The default checkpassword program from http://cr.yp.to/checkpwd.html) +checks against /etc/passwd and /etc/shadow, but the interface is general +enough to make it possible to plug in different authentication, even +RADIUS or LDAP or whatever you had in mind. You just have to write the +trivial checkpassword utility for your authentication method. I plan to +write a few small checkpassword variants to allow for shadowed MD5 +passwords from a CBD database, for example. + +This design has the following drawbacks: + + 1. since checkpassword exits on error, there is no "bad + username/password pair" error message. + 2. For the same reason, there is no 1 second delay on bad passwords. + +We can't wrap checkpassword, because once the wrapper exits, init +respawns fgetty. The only good solution would be to make a +checkpassword that prints the error message and exits only after a one +second delay. + + +login and login2 do _not_ mess with the tty or apply /etc/environment. +These are prime cases of unnecessary features in my eyes. Do that in +your shell startup files or whatever. diff --git a/README.PAM b/README.PAM new file mode 100644 index 0000000..3c95aea --- /dev/null +++ b/README.PAM @@ -0,0 +1,28 @@ +I received an email and a patch on Sep 15 2002. I incorporated the +patch, here is the email. + + From: <kromJx@crosswinds.net> + To: <web@fefe.de> + Subject: fgetty-0.6: a checkpassword with PAM support + + Hi Felix, + + I put together a checkpassword-pam program. I understand that + you don't like PAM, but nevertheless it could be useful to + someone else besides me. It is based on your checkpassword program + and the routines found in support-pam.c of the checkpassword-pam-0.95 + program. + + Feel free to edit/distribute/(or even ignore it) :-) + + Thanks for writing/sharing such great non-bloated tools. + + - J + +To build it, say + + $ make checkpassword-pam + +Please note that you can't link checkpassword-pam with the diet libc, +and I could not test the patch because I don't have PAM installed on any +box. diff --git a/checkpassword-pam.c b/checkpassword-pam.c new file mode 100644 index 0000000..bbd012d --- /dev/null +++ b/checkpassword-pam.c @@ -0,0 +1,74 @@ +#include <string.h> +#include <unistd.h> +#include <pwd.h> + +extern char** environ; + +unsigned int fmt_ulong(char *dest,unsigned long i) { + register unsigned long len,tmp,len2; + /* first count the number of bytes needed */ + for (len=1, tmp=i; tmp>9; ++len) tmp/=10; + if (dest) + for (tmp=i, dest+=len, len2=len+1; --len2; tmp/=10) + *--dest = (tmp%10)+'0'; + return len; +} + +int main(int argc,char* argv[]) { + char buf[513]; + char* last; + int len; + char *login,*ptr; + struct passwd *pw; + + if (!argv[1]) return 2; + for (len=0; len<512; ) { + int tmp; + tmp=read(3,buf+len,512-len); + if (tmp==-1) return 111; + if (tmp==0) break; + len+=tmp; + } + close(3); + buf[len]=0; last=buf+len; + login=buf; + if ((pw=getpwnam(login))) { + ptr=login+strlen(login)+1; + if (!authenticate_using_pam("login", login, ptr)) { + char **env,**ep, *eptty; + char buf[100]; + for (len=0; environ[len]; ++len) ; + env=alloca((len+4)*sizeof(char*)); + ep=env; + for (len=0; environ[len]; ++len) { + if (!strncmp(environ[len],"USER=",5)) continue; + if (!strncmp(environ[len],"HOME=",5)) continue; + if (!strncmp(environ[len],"SHELL=",6)) continue; + if (!strncmp(environ[len],"UID=",4)) continue; + *ep=environ[len]; ++ep; + } + *ep=alloca(strlen(pw->pw_shell)+7); strcat(strcpy(*ep,"SHELL="),pw->pw_shell); ++ep; + *ep=alloca(strlen(login)+6); strcat(strcpy(*ep,"USER="),login); ++ep; + *ep=alloca(strlen(pw->pw_dir)+7); strcat(strcpy(*ep,"HOME="),pw->pw_dir); ++ep; + strcpy(buf,"UID="); + buf[4+fmt_ulong(buf+4,pw->pw_uid)]=0; + *ep=buf; ++ep; + *ep=0; + + ptr+=strlen(ptr)+1; /* skip password */ + + if (initgroups(pw->pw_name,pw->pw_gid)==-1) return 1; +/* if (setgroups(1,&pw->pw_gid)==-1) return 1; */ + if (setgid(pw->pw_gid)==-1) return 1; + if (ptr) { + ptr+=strlen(ptr)+1; /* skip timestamp */ + if (ptr>=last) ptr=0; + } + if (!ptr || strcmp(ptr,"nosetuid")) if (setuid(pw->pw_uid)==-1) return -1; + if (chdir(pw->pw_dir)==-1) return 111; + execve(argv[1],argv+1,env); + return 111; + } + } + return 1; +} diff --git a/checkpassword-pam2.c b/checkpassword-pam2.c new file mode 100644 index 0000000..3fc7f3a --- /dev/null +++ b/checkpassword-pam2.c @@ -0,0 +1,131 @@ +/* + PAM support for a fgetty-style checkpasswd + + This file is a (slightly modified) concatenation of the files + pam-support.[ch] that come with Alexey Mahotkin's fine + "checkpassword-pam". Only the essential stuff has been kept, ie. + only what is needed to build a "checkpassword" compatible with the + one that is distributed with "fgetty". + + Thanks, Alexey! +*/ +/* + 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, 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. + + Copyright (c) Alexey Mahotkin <alexm@hsys.msk.ru> 2002 + + PAM support for checkpassword-pam + +*/ +#include <string.h> +#include <security/pam_appl.h> + +static const char* global_password; + +static int +conversation (int num_msg, const struct pam_message **msgs, + struct pam_response **resp, void *appdata_ptr) +{ + int i; + struct pam_response* responses; + (void) appdata_ptr; + + /* safety check */ + if (num_msg <= 0) { + return PAM_CONV_ERR; + } + + /* allocate array of responses */ + responses = calloc(num_msg, sizeof(struct pam_response)); + if (!responses) { + return PAM_CONV_ERR; + } + + for (i = 0; i < num_msg; i++) { + const struct pam_message *msg = msgs[i]; + struct pam_response* response = &(responses[i]); + char* style = NULL; + switch (msg->msg_style) { + case PAM_PROMPT_ECHO_OFF: style = "PAM_PROMPT_ECHO_OFF"; break; + case PAM_PROMPT_ECHO_ON: style = "PAM_PROMPT_ECHO_ON"; break; + case PAM_ERROR_MSG: style = "PAM_ERROR_MSG"; break; + case PAM_TEXT_INFO: style = "PAM_TEXT_INFO"; break; + } + + switch (msg->msg_style) { + case PAM_PROMPT_ECHO_OFF: + /* reply with password */ + response->resp = strdup(global_password); + if (!response->resp) + return PAM_CONV_ERR; + break; + + default: + return PAM_CONV_ERR; + } + response->resp_retcode = 0; + } + + *resp = responses; + + return PAM_SUCCESS; +} + + + +int +authenticate_using_pam (const char* service_name, + const char* username, + const char* password) +{ + struct pam_conv pam_conversation = { conversation, NULL }; + pam_handle_t* pamh; + int retval; + + /* to be used later from conversation() */ + global_password = password; + + /* initialize the PAM library */ + retval = pam_start(service_name, username, &pam_conversation, &pamh); + if (retval != PAM_SUCCESS) { + return 111; + } + + /* PAM_TTY is needed by the securetty module */ + retval = pam_set_item(pamh, PAM_TTY, (void *)getenv("TTY")); + if (retval != PAM_SUCCESS) { + return 1; + } + + /* Authenticate the user */ + retval = pam_authenticate(pamh, 0); + if (retval != PAM_SUCCESS) { + return 1; + } + + retval = pam_acct_mgmt(pamh, 0); + if (retval != PAM_SUCCESS) { + return 1; + } + + retval = pam_setcred(pamh, PAM_ESTABLISH_CRED); + if (retval != PAM_SUCCESS) { + return 1; + } + + /* terminate the PAM library */ + retval = pam_end(pamh, retval); + if (retval != PAM_SUCCESS) { + return 1; + } + + return 0; +} diff --git a/checkpassword.c b/checkpassword.c new file mode 100644 index 0000000..b7bbe85 --- /dev/null +++ b/checkpassword.c @@ -0,0 +1,91 @@ +#include <string.h> +#include <unistd.h> +#include <pwd.h> +#include <shadow.h> +#include <stdio.h> +#ifdef __dietlibc__ +#include <write12.h> +#else +inline void __write2(const char* message) { + write(2,message,strlen(message)); +} +#endif + +extern char** environ; + +unsigned int fmt_ulong(char *dest,unsigned long i) { + register unsigned long len,tmp,len2; + /* first count the number of bytes needed */ + for (len=1, tmp=i; tmp>9; ++len) tmp/=10; + if (dest) + for (tmp=i, dest+=len, len2=len+1; --len2; tmp/=10) + *--dest = (tmp%10)+'0'; + return len; +} + +int main(int argc,char* argv[]) { + char buf[513]; + char* last; + int len; + char *login,*passwd,*ptr; + struct passwd *pw; + struct spwd *spw; + + if (!argv[1]) return 2; + for (len=0; len<512; ) { + int tmp; + tmp=read(3,buf+len,512-len); + if (tmp==-1) return 111; + if (tmp==0) break; + len+=tmp; + } + close(3); + buf[len]=0; last=buf+len; + login=buf; + if ((pw=getpwnam(login))) { + passwd=pw->pw_passwd; + if ((spw=getspnam(login))) + passwd=spw->sp_pwdp; + ptr=login+strlen(login)+1; + if (!*passwd || !strcmp(crypt(ptr,passwd),passwd)) { + char **env,**ep; + char buf[100]; + for (len=0; environ[len]; ++len) ; + env=alloca((len+4)*sizeof(char*)); + ep=env; + for (len=0; environ[len]; ++len) { + if (!strncmp(environ[len],"USER=",5)) continue; + if (!strncmp(environ[len],"HOME=",5)) continue; + if (!strncmp(environ[len],"SHELL=",6)) continue; + if (!strncmp(environ[len],"UID=",4)) continue; + *ep=environ[len]; ++ep; + } + *ep=alloca(strlen(pw->pw_shell)+7); strcat(strcpy(*ep,"SHELL="),pw->pw_shell); ++ep; + *ep=alloca(strlen(login)+6); strcat(strcpy(*ep,"USER="),login); ++ep; + *ep=alloca(strlen(pw->pw_dir)+7); strcat(strcpy(*ep,"HOME="),pw->pw_dir); ++ep; + strcpy(buf,"UID="); + buf[4+fmt_ulong(buf+4,pw->pw_uid)]=0; + *ep=buf; ++ep; + *ep=0; + + ptr+=strlen(ptr)+1; /* skip password */ + + if (initgroups(pw->pw_name,pw->pw_gid)==-1) return 1; +/* if (setgroups(1,&pw->pw_gid)==-1) return 1; */ + if (setgid(pw->pw_gid)==-1) return 1; + if (ptr) { + ptr+=strlen(ptr)+1; /* skip timestamp */ + if (ptr>=last) ptr=0; + } + if (!ptr || strcmp(ptr,"nosetuid")) if (setuid(pw->pw_uid)==-1) return -1; + if (chdir(pw->pw_dir)==-1) return 111; + execve(argv[1],argv+1,env); + return 111; + } + } + if (isatty(0) && isatty(1)) { + __write2("checkpassword: wrong password.\n"); + sleep(5); + } + return 1; +} diff --git a/fgetty.8 b/fgetty.8 new file mode 100644 index 0000000..9c8af6d --- /dev/null +++ b/fgetty.8 @@ -0,0 +1,90 @@ +.\" Tino Reichardt <der@mcmilk.de> (2002-01-19) +.TH FGETTY 8 "2002-01-19" "fgetty" "System Administrator's Manual" +.SH "NAME" +fgetty \- a small getty for linux +.SH "SYNOPSIS" +\fBfgetty\fR \fItty\fR [\fI--noclear\fR] [\fI--long-hostname\fR] +.SH "DESCRIPTION" +The command \fBfgetty\fP is normally invoked by \fIinit(8)\fP, opens a tty port, +prompts for a login name and invokes the /bin/login command. +.SH "OPTIONS" +\fB--noclear\fR makes fgetty not clear the screen before displaying the +login prompt. + +\fB--long-hostname\fR makes fgetty display the full +hostname in the login prompt. +.SH "FILES" +.TS +tab (@); +l l. +/etc/issue@ printed before the login prompt +/etc/inittab@ \fIinit\fP(8) configuration file +/var/run/utmp@ the system status file +.TE +.SH ISSUE ESCAPES +The issue-file (\fI/etc/issue\fP or the file may contain certain escape codes to +display the system name, date and time etc. All escape codes consist of a backslash +(\\) immediately followed by one of the letters explained below. +.TP +d +the current date. (eg: 2002-01-19) +.TP +s +the system name, the name of the operating system. (eg: Linux) +.TP +l +the name of the current tty line. (eg: tty3) +.TP +m +the architecture identifier of the machine (eg: i586) +.TP +n +the nodename of the machine, also known as the hostname. (eg: sirius) +.TP +o +the domainname of the machine. (eg: (none)) +.TP +r +the release number of the OS. (eg: 2.4.3-i) +.TP +t +the current time. (eg: 14:51:51) +.TP +u +the number of current users logged in. (eg: 4) +.TP +U +the string "1 user" or "<n> users", where <n> is the number of current +users logged in. (eg: 4 users) +.TP +v +Insert the version of the OS. (eg: #2 Fre Jan 18 23:05:45 CET 2002) +.SH "ENVIRONMENT VARIABLES" +.TS +tab (@); +l l. +HOST@ is set to your hostname +TERM@ is set to linux +TTY@ is set to the current tty line +.TE +.SH "RETURN VALUES" +.TS +tab (@); +l l. +\fB1\fP @could not chown/chmod tty device +\fB3\fP @could not open tty device +\fB4\fP @not a typewriter +\fB5\fP @vhangup failed +\fB6\fP @could not open tty (can't happen) +\fB7\fP @dup failed +\fB8\fP @could not exec login +\fB9\fP @read returned an unexpected error +\fB10\fP @unprintable character in login name +\fB11\fP @login name too long (>40) +\fB13\fP @user name started with a dash +\fB23\fP @received SIGQUIT +.TE +.SH "AUTHOR" +Felix von Leitner <felix-fgetty@fefe.de> +.PP +Homepage: http://www.fefe.de/fgetty/ diff --git a/fgetty.c b/fgetty.c new file mode 100644 index 0000000..c4a6241 --- /dev/null +++ b/fgetty.c @@ -0,0 +1,319 @@ +/* This is mostly mingetty without printf */ + +#define _GNU_SOURCE +#include <sys/utsname.h> +#include <unistd.h> +#include <sys/param.h> +#include <utmp.h> +#include <fcntl.h> +#include <sys/signal.h> +#include <sys/ioctl.h> +#include <errno.h> +#include <termios.h> +#include <stdlib.h> + +#include "fmt.h" + +static struct utsname uts; +static char hn[MAXHOSTNAMELEN + 6]="HOST="; +static int hn_len=5; +static time_t cur_time; +static char *tty; + +static int noclear=0; + +void whine(const char* message) { + write(2,message,strlen(message)); +} + +void error(char *message,int exitcode) { + whine(message); + exit(exitcode); +} + +static void echo_off() { + struct termios foo; + if (!tcgetattr(0,&foo)) { + foo.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + tcsetattr(0, TCSANOW, &foo); + } +} + +int doutmp() { + off_t curpos; + struct utmp ut; + pid_t mypid=getpid(); + int fd=open(_PATH_UTMP,O_RDWR); + if (fd) { + for (;;) { + int len; + curpos=lseek(fd,0,SEEK_CUR); + len=read(fd,&ut,sizeof(ut)); + if (len!=sizeof(ut)) break; + if (ut.ut_pid==mypid || !strcmp(ut.ut_line,tty+5)) { +/* write(1,"found my utmp record\n",21); */ + break; + } + } + if (ut.ut_pid!=mypid) { + memset(&ut,0,sizeof(ut)); + ut.ut_pid=mypid; + memcpy(ut.ut_id,tty+3,sizeof(ut.ut_id)); + } + memcpy(ut.ut_user,"LOGIN",6); + memcpy(ut.ut_line,tty+5,sizeof(ut.ut_line)); + ut.ut_tv.tv_sec=cur_time; + ut.ut_type=LOGIN_PROCESS; + lseek(fd,curpos,SEEK_SET); + write(fd,&ut,sizeof(ut)); + close(fd); + } + if ((fd=open(_PATH_WTMP,O_APPEND|O_WRONLY))>=0) { + write(fd,&ut,sizeof(ut)); + close(fd); + } +} + +void sigquit_handler(int signum) { + error("SIGQUIT received\n",23); +} + +void open_tty() { + struct sigaction sa; + int fd; + if (chown(tty,0,0) || chmod(tty,0600)) + error("fgetty: could not chown/chmod tty device\n",1); + sa.sa_handler=SIG_IGN; + sa.sa_flags=0; + sigemptyset(&sa.sa_mask); + sigaction(SIGHUP,&sa,NULL); + sa.sa_handler=sigquit_handler; + sigaction(SIGQUIT,&sa,NULL); + setsid(); + if ((fd=open(tty, O_RDWR, 0))<0) + error("fgetty: could not open tty device\n",3); + if (!isatty(fd)) + error("fgetty: \"not a typewriter\" ;-)\n",4); + if (ioctl (fd, TIOCSCTTY, (void *)1)==0) { + if (vhangup()) /* linux specific */ + error("fgetty: vhangup failed\n",5); + } else + whine("fgetty: warning: could not set controlling tty!\n"); + close(2); close(1); close(0); close(fd); + if (open(tty,O_RDWR,0) != 0) + error("fgetty: could not open tty\n",6); + if (dup(0) != 1 || dup(0) != 2) + error("could not dup stdout and stderr\n",7); + if (!noclear) + write(0,"\033c",2); /* linux specific */ + sa.sa_handler=SIG_DFL; + sa.sa_flags=0; + sigemptyset(&sa.sa_mask); + sigaction(SIGHUP,&sa,NULL); +} + +void output_special_char(char c) { + switch (c) { + case 's': write(1,uts.sysname,strlen(uts.sysname)); break; + case 'n': write(1,uts.nodename,strlen(uts.nodename)); break; + case 'r': write(1,uts.release,strlen(uts.release)); break; + case 'v': write(1,uts.version,strlen(uts.version)); break; + case 'm': write(1,uts.machine,strlen(uts.machine)); break; + case 'o': write(1,uts.domainname,strlen(uts.domainname)); break; + case 't': + case 'd': + { + time_t now; + struct tm *tm; + char buf[30]; + char *tmp; + + time (&now); + tm = localtime (&now); + if (c == 'd') { + tmp=buf+fmt_ulong(buf,tm->tm_year+1900); + *tmp++='-'; + tm->tm_mon++; + *tmp++=tm->tm_mon/10+'0'; + *tmp++=tm->tm_mon%10+'0'; + *tmp++='-'; + *tmp++=tm->tm_mday/10+'0'; + *tmp++=tm->tm_mday%10+'0'; + *tmp++=0; + write(1,buf,strlen(buf)); +#if 0 + /* ISO 8601 */ + printf ("%d-%02d-%02d", tm->tm_year, + tm->tm_mon+1, tm->tm_mday); +#endif + } else { + buf[0]=tm->tm_hour/10+'0'; + buf[1]=tm->tm_hour%10+'0'; + buf[2]=':'; + buf[3]=tm->tm_min/10+'0'; + buf[4]=tm->tm_min%10+'0'; + buf[5]=':'; + buf[6]=tm->tm_sec/10+'0'; + buf[7]=tm->tm_sec%10+'0'; + write(1,buf,8); + } +#if 0 + tmp=buf; + printf ("%02d:%02d:%02d", + tm->tm_hour, tm->tm_min, tm->tm_sec); +#endif + break; + } + case 'l': write(1,tty+5,strlen(tty)-5); break; + case 'u': + case 'U': + { + int users=0; + struct utmp ut; + int fd=open(_PATH_UTMP,O_RDWR); + char buf[20]; + if (fd) { + for (;;) { + int len; + len=read(fd,&ut,sizeof(ut)); + if (len!=sizeof(ut)) break; + if (ut.ut_type == USER_PROCESS) users++; + } + close(fd); + } + write(1,buf,fmt_ulong(buf,users)); + if (c=='U') { + if (users==1) + write(1," user",5); + else + write(1," users",6); + } + } + break; + default: + write(1,&c,1); + } +} + +void do_prompt() { + int fd=open("/etc/issue",O_RDONLY); + char *buf; + off_t length; + write(1,"\n",1); + if (fd) { + char *c,*last; + length=lseek(fd,0,SEEK_END); + lseek(fd,0,SEEK_SET); + buf=alloca(length+1); + read(fd,buf,length); + close(fd); + last=buf+length; + for (c=buf; c<last; c++) { + if (*c=='\\') + output_special_char(*++c); + else + write(1,c,1); + } + } + + write(1,hn+5,hn_len-5); + write(1," login: ",8); +} + +static inline int _isprint(char c) { + return ((c>='A' && c<='Z') || + (c>='a' && c<='z') || + (c>='0' && c<='9') || + (c=='_' || c=='.' || c==',' || c=='-')); +} + +char *get_logname() { + static char logname[40]; + char *c; + ioctl(0, TCFLSH, 0); /* flush pending input */ + for (*logname=0; *logname==0; ) { + do_prompt(); + for (c=logname;;) { + if (read(0,c,1)<1) { + if (errno==EINTR || errno==EIO || errno==ENOENT) + exit(0); + error("received strange error\n",9); + } + if (*c == '\n' || *c == '\r') { + *c=0; + break; + } else if (!_isprint(*c)) + error("unprintable character in login name\n",10); + else if (c-logname >= sizeof(logname)-1) + error("login name too long\n",11); + else + c++; + } + } +#if 0 + write(1,"\n\ngot name ",11); + write(1,logname,strlen(logname)); + write(1,"\n\n",2); +#endif + return logname; +} + +extern char ** environ; + +char ttybuf[20]="/dev/"; +char ttybuf2[25]="TTY="; + +int main(int argc,char *argv[]) { + char *loginargv[]={"/bin/login", "--", 0, 0}; + char *logname; + int i; + char hostname_end='.'; + tty=argv[1]; + if (!tty) + error("usage: fgetty 1\n" + " fgetty vc/1\n" + " fgetty /dev/tty1\n",111); + if (tty[0]=='/') + strncpy(ttybuf,tty,15); + else if (isdigit(tty[0])) { + struct stat ss; + /* try prepending /dev/vc/1 and /dev/tty1 */ + strcpy(ttybuf,"/dev/vc/"); strncpy(ttybuf+8,tty,3); + if (stat(ttybuf,&ss) && errno==ENOENT) { + ttybuf[5]=ttybuf[6]='t'; ttybuf[7]='y'; + } + } else + strncpy(ttybuf+5,tty,10); + tty=ttybuf; + strcpy(ttybuf2+4,ttybuf); + + uname(&uts); + if (gethostname(hn+5, MAXHOSTNAMELEN)!=0) hn[5]=0; + hn[5+MAXHOSTNAMELEN]=0; + putenv("TERM=linux"); + putenv(ttybuf2); + putenv(hn); + time(&cur_time); + for (i=2; i<argc; ++i) { + if (!strcmp(argv[i],"--noclear")) + noclear=1; + else if (!strcmp(argv[i],"--long-hostname")) + hostname_end=0; + } + while (hn[hn_len]!=0 && hn[hn_len]!=hostname_end) ++hn_len; +#ifndef DEBUG + doutmp(); + open_tty(); +#endif + ioctl(0,TCFLSH,2); /* mingetty says this is important for modem users */ + while ((logname=get_logname()) == 0); + if (logname[0]=='-') error("username may not start with a dash\n",13); + loginargv[2]=logname; + echo_off(); +#ifdef TEST + execve("/bin/login1", loginargv, environ); +#else + execve("/bin/login", loginargv, environ); +#endif + exit(8); +} @@ -0,0 +1,25 @@ +#ifndef FMT_H +#define FMT_H + +#define FMT_ULONG 40 /* enough space to hold 2^128 - 1 in decimal, plus \0 */ +#define FMT_LEN ((char *) 0) /* convenient abbreviation */ + +extern unsigned int fmt_uint(char *,unsigned int); +extern unsigned int fmt_uint0(char *,unsigned int,unsigned int); +extern unsigned int fmt_xint(char *,unsigned int); +extern unsigned int fmt_nbbint(char *,unsigned int,unsigned int,unsigned int,unsigned int); +extern unsigned int fmt_ushort(char *,unsigned short); +extern unsigned int fmt_xshort(char *,unsigned short); +extern unsigned int fmt_nbbshort(char *,unsigned int,unsigned int,unsigned int,unsigned short); +extern unsigned int fmt_ulong(char *,unsigned long); +extern unsigned int fmt_xlong(char *,unsigned long); +extern unsigned int fmt_nbblong(char *,unsigned int,unsigned int,unsigned int,unsigned long); + +extern unsigned int fmt_plusminus(char *,int); +extern unsigned int fmt_minus(char *,int); +extern unsigned int fmt_0x(char *,int); + +extern unsigned int fmt_str(char *,const char *); +extern unsigned int fmt_strn(char *,const char *,unsigned int); + +#endif diff --git a/fmt_ulong.c b/fmt_ulong.c new file mode 100644 index 0000000..db48bfd --- /dev/null +++ b/fmt_ulong.c @@ -0,0 +1,13 @@ +#include "fmt.h" + +unsigned int fmt_ulong(register char *s,register unsigned long u) +{ + register unsigned int len; register unsigned long q; + len = 1; q = u; + while (q > 9) { ++len; q /= 10; } + if (s) { + s += len; + do { *--s = '0' + (u % 10); u /= 10; } while(u); /* handles u == 0 */ + } + return len; +} @@ -0,0 +1,149 @@ +/* diet login without all the bloat but with checkpassword for pluggable + * authentication support. */ + +/* algorithm: + 1. argv[1] is the user name + (optional: argv[2-argc] are environment variables) + 2. print "user's password: " to stdout + 3. set terminal to "don't echo" + 4. read password + 5. set terminal to "echo" + 6. fork and run checkpassword login2 + 7. wait() for checkpassword + 8. if checkpassword returns 1, the password was wrong. sleep(1) and ++counter + 9. if counter reaches 5, sleep(5) and exit + + login2 is expected to: + + 1. kill ppid (i.e. this process) if it is run. + 2. print motd unless (-f .hushlogin) + 3. check for mail (maybe) + 4. set TERM according to /etc/ttytypes + 5. edit utmp and wtmp (uh-oh, how should we do this? We are not root any more!) + 6. exec $SHELL + */ + +#define CHECKPASSWORD "/bin/checkpassword.login" +#define FALLBACKCHECKPASSWORD "/bin/checkpassword" +#define LOGIN2 "/bin/login2" + +#include <string.h> +#include <unistd.h> +#include <termios.h> +#include <stdlib.h> +#include <errno.h> +#include <utmp.h> +#include <fcntl.h> +#include <signal.h> +#include <write12.h> + +void die(const char *message) { + __write2(message); __write2("\n"); + exit(1); +} + +struct termios oldtermios; + +static void echo_off() { + struct termios foo; + if (tcgetattr(0,&oldtermios)) + die("tcgetattr failed"); + foo=oldtermios; + foo.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); + tcsetattr(0, TCSANOW, &foo); +} + +static void echo_on() { + oldtermios.c_lflag |= ECHO | ECHOE | ECHOK; + tcsetattr(0, TCSANOW, &oldtermios); +} + +main(int argc,char *argv[]) { + int filedes[2]; + char *username=argv[1]; + char *buf; + char __username[100]; + char password[100]; + int pwlen; + char *Argv[]={"checkpassword",LOGIN2,0}; + pid_t child; + int utmpfd,wtmpfd; + { + int i=1; + while (username && username[0]=='-') + username=argv[++i]; + } + + if (!username) { + char *host=getenv("HOST"); + if (host) { + int len=strlen(host); + host[len]=' '; + write(0,host,len+1); + host[len]=0; + } + __write1("login: "); + pwlen=read(0,__username,9); + if (pwlen<0) die("read failed"); + __username[pwlen-1]=0; /* skip newline */ + username=__username; + } + { + buf=alloca(strlen(username)+20); + strcpy(buf,username); + strcat(buf,"'s password: "); + __write1(buf); + } + echo_off(); + pwlen=read(0,password,99); + if (pwlen<0) die("read failed"); + password[pwlen-1]=0; + echo_on(); + __write1("\n"); + + if (pipe(filedes)) + die("pipe failed"); + if (filedes[0]!=3) + die("pipe did not return fd 3"); + switch (child=fork()) { + case -1: + die("login: could not fork"); + case 0: + /* child */ + close(3); + { + char buf[512]; + int len; + len=strlen(username)+1; + strcpy(buf,username); + strlcpy(buf+len,password,512-len); + len+=strlen(password)+1; +/* buf[len++]='Y'; */ + len+=__ltostr(buf+len,512-len,time(0),10,0); + buf[len]=0; + if (len<400) { + strcpy(buf+len+1,"nosetuid"); + len+=9; + } + write(4,buf,len+1); + close(4); + } + break; + default: + close(4); + utmpfd=open(_PATH_UTMP,O_RDWR); + if (utmpfd==-1) utmpfd=open("/dev/null",O_RDWR); + if (utmpfd>0 && utmpfd!=4) { dup2(utmpfd,4); close(utmpfd); utmpfd=4; }; + wtmpfd=open(_PATH_WTMP,O_APPEND|O_WRONLY); + if (wtmpfd==-1) wtmpfd=open("/dev/null",O_WRONLY); + if (wtmpfd>0 && wtmpfd!=5) { dup2(wtmpfd,5); close(wtmpfd); wtmpfd=5; }; + if (utmpfd!=4 || wtmpfd!=5) { + close(utmpfd); close(wtmpfd); + __write2("utmpfd!=4 || wtmpfd!=5\n"); + } + execve(CHECKPASSWORD,Argv,environ); + if (errno==ENOENT) + execve(FALLBACKCHECKPASSWORD,Argv,environ); + die("login: could not exec checkpassword"); + } +} diff --git a/login2.c b/login2.c new file mode 100644 index 0000000..8aaf6d6 --- /dev/null +++ b/login2.c @@ -0,0 +1,110 @@ +/* diet login without all the bloat but with checkpassword for pluggable + * authentication support. */ + +/* algorithm: + 1. print motd unless (-f .hushlogin) + 2. check for mail (maybe) + 3. set TERM according to /etc/ttytypes + 4. edit utmp and wtmp (passed as fd 4 and fd 5) + 5. exec $SHELL + */ + +#include <string.h> +#include <unistd.h> +#include <stdlib.h> +#include <fcntl.h> +#include <utmp.h> +#include <grp.h> +#include <write12.h> + +#include <sys/stat.h> + +void die(const char *message) { + __write2(message); __write2("\n"); + exit(1); +} + +extern char **environ; + +int doutmp(const char *login) { + off_t curpos; + struct utmp ut; + pid_t mypid=getpid(); + int fd=4; + for (;;) { + int len; + curpos=lseek(fd,0,SEEK_CUR); + len=read(fd,&ut,sizeof(ut)); + if (len!=sizeof(ut)) break; + if (ut.ut_pid==mypid) { +/* write(1,"found my utmp record\n",21); */ + break; + } + } + if (ut.ut_pid!=mypid) { + memset(&ut,0,sizeof(ut)); + ut.ut_pid=mypid; + memcpy(ut.ut_id,getenv("TTY")+4+3,sizeof(ut.ut_id)); + } + strncpy(ut.ut_user,login,sizeof(ut.ut_user)); + ut.ut_tv.tv_sec=time(0); + ut.ut_type=USER_PROCESS; + lseek(fd,curpos,SEEK_SET); + write(fd,&ut,sizeof(ut)); + close(4); + write(5,&ut,sizeof(ut)); + close(5); +} + +main(int argc,char *argv[]) { + int fd; + char *shell=getenv("SHELL"); + char *Argv[]={"-sh",0}; + char *login=getenv("USER"); + if (getuid()==0) { /* checkpassword honored "nosetuid" */ + char *tmp=getenv("UID"); + char *tty=getenv("TTY"); + if (tmp) { + uid_t u=strtoul(tmp,&tmp,10); + struct group *g=getgrnam("console"); + gid_t gid=getgid(); + if (*tmp==0) + chown(tty,u,gid); + initgroups(login,g?g->gr_gid:gid); + /* if it fails, too bad. checkpassword should already have made + * sure no additional groups are there */ + setuid(u); + if (u && getuid()!=u) { die("getuid() != u!\n"); return 2; } + } else return 2; + } + close(11); + if ((fd=open(".hushlogin",O_RDONLY))>=0) + close(fd); + else { + if ((fd=open("/etc/motd",O_RDONLY))>=0) { + char buf[1024]; + int len; + while ((len=read(fd,buf,1024))>0) + write(0,buf,len); + close(fd); + } + } + /* login passes open utmp and wtmp on fd #4 and #5 */ + doutmp(login); + { + char *buf=alloca(strlen(login)+20); + strcpy(buf,"LOGNAME="); + strcat(buf,login); + putenv(buf); + if (shell) { + char *tmp=strrchr(shell,'/'); + char *argv0=alloca(strlen(shell)+2); + if (tmp) ++tmp; else tmp=shell; + strcpy(argv0+1,tmp); + *argv0='-'; + Argv[0]=argv0; + execve(shell,Argv,environ); + } + execve("/bin/sh",Argv,environ); + } +} |