diff options
author | Todd C. Miller <millert@cvs.openbsd.org> | 1996-10-14 05:14:59 +0000 |
---|---|---|
committer | Todd C. Miller <millert@cvs.openbsd.org> | 1996-10-14 05:14:59 +0000 |
commit | 4403da4c4135881add7d2a5b81282fbcb01ae47c (patch) | |
tree | 55ab58992d6e1d1b00c327d278ec2f1b9947d6fb /gnu | |
parent | 0a64da4f22aa6e7dc9ce23604386381860186ad4 (diff) |
sudo 1.5.2
Diffstat (limited to 'gnu')
32 files changed, 8665 insertions, 0 deletions
diff --git a/gnu/usr.bin/sudo/COPYING b/gnu/usr.bin/sudo/COPYING new file mode 100644 index 00000000000..a43ea2126fb --- /dev/null +++ b/gnu/usr.bin/sudo/COPYING @@ -0,0 +1,339 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 675 Mass Ave, Cambridge, MA 02139, 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 + + Appendix: 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) 19yy <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., 675 Mass Ave, Cambridge, MA 02139, 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) 19yy 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/gnu/usr.bin/sudo/Makefile b/gnu/usr.bin/sudo/Makefile new file mode 100644 index 00000000000..7b76abe73aa --- /dev/null +++ b/gnu/usr.bin/sudo/Makefile @@ -0,0 +1,5 @@ +# $OpenBSD: Makefile,v 1.1 1996/10/14 05:14:41 millert Exp $ + +SUBDIR+=sudo visudo + +.include <bsd.subdir.mk> diff --git a/gnu/usr.bin/sudo/README b/gnu/usr.bin/sudo/README new file mode 100644 index 00000000000..c73e4fbce4b --- /dev/null +++ b/gnu/usr.bin/sudo/README @@ -0,0 +1,3 @@ +This is a minimal sudo distribution for OpenBSD. You can get the +full package at ftp://ftp.courtesan.com/pub/sudo/. For info on +sudo please see http://www.courtesan.com/courtesan/products/sudo/. diff --git a/gnu/usr.bin/sudo/sudo/Makefile b/gnu/usr.bin/sudo/sudo/Makefile new file mode 100644 index 00000000000..4770a031d76 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/Makefile @@ -0,0 +1,41 @@ +# $OpenBSD: Makefile,v 1.1 1996/10/14 05:14:43 millert Exp $ + +PROG= sudo +MAN= sudo.8 sudoers.5 +CFLAGS+=-I${.CURDIR} -I. +SRCS= check.c find_path.c getspwuid.c goodpath.c interfaces.c logging.c parse.c sudo.c sudo_setenv.c tgetpass.c y.tab.c lex.yy.c +CLEANFILES+=y.tab.c y.tab.h lex.yy.c + +LDADD= -lcompat +DPADD= ${LIBCOMPAT} + +.include <bsd.own.mk> # For SKEY, KERBEROS and KERBEROS5 + +.if defined(SKEY) +CFLAGS+=-DHAVE_SKEY +LDADD+= -lskey +DPADD+= ${LIBSKEY} +.endif + +.if defined(KERBEROS5) +CFLAGS+= -DHAVE_KERB5 +LDADD+= -lkrb5 -lcrypto +DPADD+= ${LIBKRB5} ${LIBCRYPTO} +.elif defined(KERBEROS) +CFLAGS+= -DHAVE_KERB4 +LDADD+= -lkrb -ldes +DPADD+= ${LIBKRB} ${LIBDES} +.endif + +BINOWN= root +BINMODE=4111 +BINDIR?=/usr/bin + +.include <bsd.prog.mk> + +lex.yy.c: parse.lex + rm -f lex.yy.c + $(LEX) ${.CURDIR}/parse.lex + +y.tab.c y.tab.h: parse.yacc + $(YACC) -d ${.CURDIR}/parse.yacc diff --git a/gnu/usr.bin/sudo/sudo/check.c b/gnu/usr.bin/sudo/sudo/check.c new file mode 100644 index 00000000000..3c6373252f5 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/check.c @@ -0,0 +1,860 @@ +/* + * CU sudo version 1.5.2 (based on Root Group sudo version 1.1) + * + * This software comes with no waranty whatsoever, use at your own risk. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + */ + +/* + * sudo version 1.1 allows users to execute commands as root + * Copyright (C) 1991 The Root Group, Inc. + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ******************************************************************* + * + * check.c + * + * check_user() only returns if the user's timestamp file + * is current or if they enter a correct password. + * + * Jeff Nieusma Thu Mar 21 22:39:07 MST 1991 + */ + +#ifndef lint +static char rcsid[] = "$Id: check.c,v 1.1 1996/10/14 05:14:43 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#include <fcntl.h> +#include <sys/param.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/file.h> +#include <netinet/in.h> +#include <pwd.h> +#include <grp.h> +#include "sudo.h" +#include <options.h> +#include "insults.h" +#if (SHADOW_TYPE == SPW_SECUREWARE) +# ifdef __hpux +# include <hpsecurity.h> +# else +# include <sys/security.h> +# endif /* __hpux */ +# include <prot.h> +#endif /* SPW_SECUREWARE */ +#ifdef HAVE_KERB4 +# include <krb.h> +#endif /* HAVE_KERB4 */ +#ifdef HAVE_AFS +# include <afs/stds.h> +# include <afs/kautils.h> +#endif /* HAVE_AFS */ +#ifdef HAVE_SECURID +# include <sdi_athd.h> +# include <sdconf.h> +# include <sdacmvls.h> +#endif /* HAVE_SECURID */ +#ifdef HAVE_SKEY +# include <skey.h> +#endif /* HAVE_SKEY */ +#ifdef HAVE_OPIE +# include <opie.h> +#endif /* HAVE_OPIE */ +#ifdef HAVE_UTIME +# ifdef HAVE_UTIME_H +# include <utime.h> +# endif /* HAVE_UTIME_H */ +#else +# include "emul/utime.h" +#endif /* HAVE_UTIME */ + + +/* + * Prototypes for local functions + */ +static int check_timestamp __P((void)); +static void check_passwd __P((void)); +static int touch __P((char *)); +static void update_timestamp __P((void)); +static void reminder __P((void)); +#ifdef HAVE_KERB4 +static int sudo_krb_validate_user __P((struct passwd *, char *)); +#endif /* HAVE_KERB4 */ +#ifdef HAVE_SKEY +static char *sudo_skeyprompt __P((struct skey *, char *)); +#endif /* HAVE_SKEY */ +#ifdef HAVE_OPIE +static char *sudo_opieprompt __P((struct opie *, char *)); +#endif /* HAVE_OPIE */ +int user_is_exempt __P((void)); + +/* + * Globals + */ +static int timedir_is_good; +static char timestampfile[MAXPATHLEN + 1]; +#ifdef HAVE_SECURID +union config_record configure; +#endif /* HAVE_SECURID */ +#ifdef HAVE_SKEY +struct skey skey; +#endif +#ifdef HAVE_OPIE +struct opie opie; +#endif +#if (SHADOW_TYPE == SPW_SECUREWARE) && defined(__alpha) +extern uchar_t crypt_type; +#endif /* SPW_SECUREWARE && __alpha */ + + + +/******************************************************************** + * + * check_user() + * + * This function only returns if the user can successfully + * verify who s/he is. + */ + +void check_user() +{ + register int rtn; + mode_t oldmask; + + if (user_is_exempt()) /* some users don't need to enter a passwd */ + return; + + oldmask = umask(077); /* make sure the timestamp files are private */ + + rtn = check_timestamp(); + if (rtn && user_uid) { /* if timestamp is not current... */ +#ifndef NO_MESSAGE + if (rtn == 2) + reminder(); /* do the reminder if ticket file is new */ +#endif /* NO_MESSAGE */ + check_passwd(); + } + + update_timestamp(); + (void) umask(oldmask); /* want a real umask to exec() the command */ + +} + + + +/******************************************************************** + * + * user_is_exempt() + * + * this function checks the user is exempt from supplying a password. + */ + +int user_is_exempt() +{ +#ifdef EXEMPTGROUP + struct group *grp; + char **gr_mem; + + if ((grp = getgrnam(EXEMPTGROUP)) == NULL) + return(FALSE); + + if (getgid() == grp->gr_gid) + return(TRUE); + + for (gr_mem = grp->gr_mem; *gr_mem; gr_mem++) { + if (strcmp(user_name, *gr_mem) == 0) + return(TRUE); + } + + return(FALSE); +#else + return(FALSE); +#endif +} + + + +/******************************************************************** + * + * check_timestamp() + * + * this function checks the timestamp file. If it is within + * TIMEOUT minutes, no password will be required + */ + +static int check_timestamp() +{ + register char *p; + struct stat statbuf; + register int timestamp_is_old = -1; + time_t now; + +#ifdef USE_TTY_TICKETS + if (p = strrchr(tty, '/')) + p++; + else + p = tty; + + (void) sprintf(timestampfile, "%s/%s.%s", _PATH_SUDO_TIMEDIR, user_name, p); +#else + (void) sprintf(timestampfile, "%s/%s", _PATH_SUDO_TIMEDIR, user_name); +#endif /* USE_TTY_TICKETS */ + + timedir_is_good = 1; /* now there's an assumption for ya... */ + + /* become root */ + set_perms(PERM_ROOT, 0); + + /* + * walk through the path one directory at a time + */ + for (p = timestampfile + 1; (p = strchr(p, '/')); *p++ = '/') { + *p = '\0'; + if (stat(timestampfile, &statbuf) < 0) { + if (strcmp(timestampfile, _PATH_SUDO_TIMEDIR)) + (void) fprintf(stderr, "Cannot stat() %s\n", timestampfile); + timedir_is_good = 0; + *p = '/'; + break; + } + } + + /* + * if all the directories are stat()able + */ + if (timedir_is_good) { + /* + * last component in _PATH_SUDO_TIMEDIR must be owned by root + * and mode 0700 or we ignore the timestamps in it. + */ + if (statbuf.st_uid != 0 || (statbuf.st_mode & 0000077)) { + timedir_is_good = 0; + timestamp_is_old = 2; + log_error(BAD_STAMPDIR); + inform_user(BAD_STAMPDIR); + } else if (stat(timestampfile, &statbuf)) { + /* timestamp file does not exist? */ + timestamp_is_old = 2; /* return (2) */ + } else { + /* check the time against the timestamp file */ + now = time((time_t *) NULL); + if (TIMEOUT && now - statbuf.st_mtime < 60 * TIMEOUT) + /* check for bogus time on the stampfile */ + if (statbuf.st_mtime > now + 60 * TIMEOUT * 2) { + timestamp_is_old = 2; /* bogus time value */ + log_error(BAD_STAMPFILE); + inform_user(BAD_STAMPFILE); + } else { + timestamp_is_old = 0; /* time value is reasonable */ + } + else + timestamp_is_old = 1; /* else make 'em enter password */ + } + } + /* + * there was a problem stat()ing a directory + */ + else { + timestamp_is_old = 2; /* user has to enter password + reminder */ + /* make the TIMEDIR directory */ + if (mkdir(_PATH_SUDO_TIMEDIR, S_IRWXU)) { + perror("check_timestamp: mkdir"); + timedir_is_good = 0; + } else { + timedir_is_good = 1; /* _PATH_SUDO_TIMEDIR now exists */ + } + } + + /* relinquish root */ + set_perms(PERM_USER, 0); + + return (timestamp_is_old); +} + + + +/******************************************************************** + * + * touch() + * + * This function updates the access and modify times on a file + * via utime(2). + */ + +static int touch(file) + char *file; +{ +#if defined(HAVE_UTIME) && !defined(HAVE_UTIME_NULL) +#ifdef HAVE_UTIME_POSIX +#define UTP (&ut) + struct utimbuf ut; + + ut.actime = ut.modtime = time(NULL); +#else +#define UTP (ut) + /* old BSD <= 4.3 has no struct utimbuf */ + time_t ut[2]; + + ut[0] = ut[1] = time(NULL); +#endif /* HAVE_UTIME_POSIX */ +#else +#define UTP NULL +#endif /* HAVE_UTIME && !HAVE_UTIME_NULL */ + + return(utime(file, UTP)); +} +#undef UTP + + + +/******************************************************************** + * + * update_timestamp() + * + * This function changes the timestamp to "now" + */ + +static void update_timestamp() +{ + if (timedir_is_good) { + /* become root */ + set_perms(PERM_ROOT, 0); + + if (touch(timestampfile) < 0) { + int fd = open(timestampfile, O_WRONLY | O_CREAT | O_TRUNC, 0600); + + if (fd < 0) + perror("update_timestamp: open"); + else + close(fd); + } + + /* relinquish root */ + set_perms(PERM_USER, 0); + } +} + + + +/******************************************************************** + * + * remove_timestamp() + * + * This function removes the timestamp ticket file + */ + +void remove_timestamp() +{ +#ifdef USE_TTY_TICKETS + char *p; + + if (p = strrchr(tty, '/')) + p++; + else + p = tty; + + (void) sprintf(timestampfile, "%s/%s.%s", _PATH_SUDO_TIMEDIR, user_name, p); +#else + (void) sprintf(timestampfile, "%s/%s", _PATH_SUDO_TIMEDIR, user_name); +#endif /* USE_TTY_TICKETS */ + + /* become root */ + set_perms(PERM_ROOT, 0); + + /* remove the ticket file */ + (void) unlink(timestampfile); + + /* relinquish root */ + set_perms(PERM_USER, 0); +} + + + +/******************************************************************** + * + * check_passwd() + * + * This function grabs the user's password and checks with the password + * in /etc/passwd (or uses other specified authentication method). + */ + +#ifdef HAVE_SECURID +static void check_passwd() +{ + struct SD_CLIENT sd_dat, *sd; /* SecurID data block */ + register int counter = TRIES_FOR_PASSWORD; + + (void) memset ((VOID *)&sd_dat, 0, sizeof(sd_dat)); + sd = &sd_dat; + + /* Initialize SecurID. */ + set_perms(PERM_ROOT, 0); + creadcfg(); + if (sd_init(sd) != 0) { + (void) fprintf(stderr, "%s: Cannot contact SecurID server\n", Argv[0]); + exit(1); + } + + /* + * you get TRIES_FOR_PASSWORD times to guess your password + */ + while (counter > 0) { + if (sd_auth(sd) == ACM_OK) { + set_perms(PERM_USER, 0); + return; + } + + --counter; /* otherwise, try again */ +#ifdef USE_INSULTS + (void) fprintf(stderr, "%s\n", INSULT); +#else + (void) fprintf(stderr, "%s\n", INCORRECT_PASSWORD); +#endif /* USE_INSULTS */ + } + set_perms(PERM_USER, 0); + + if (counter > 0) { + log_error(PASSWORD_NOT_CORRECT); + inform_user(PASSWORD_NOT_CORRECT); + } else { + log_error(PASSWORDS_NOT_CORRECT); + inform_user(PASSWORDS_NOT_CORRECT); + } + + exit(1); +} +#else /* !HAVE_SECURID */ +static void check_passwd() +{ + char *pass; /* this is what gets entered */ + register int counter = TRIES_FOR_PASSWORD; +#if defined(HAVE_KERB4) && defined(USE_GETPASS) + char kpass[_PASSWD_LEN + 1]; +#endif /* HAVE_KERB4 && USE_GETPASS */ + +#ifdef HAVE_SKEY + (void) memset((VOID *)&skey, 0, sizeof(skey)); +#endif /* HAVE_SKEY */ +#ifdef HAVE_OPIE + (void) memset((VOID *)&opie, 0, sizeof(opie)); +#endif /* HAVE_OPIE */ + + /* + * you get TRIES_FOR_PASSWORD times to guess your password + */ + while (counter > 0) { + +#ifdef HAVE_SKEY + /* rewrite the prompt if using s/key since the challenge can change */ + set_perms(PERM_ROOT, 0); + prompt = sudo_skeyprompt(&skey, prompt); + set_perms(PERM_USER, 0); +#endif /* HAVE_SKEY */ +#ifdef HAVE_OPIE + /* rewrite the prompt if using OPIE since the challenge can change */ + set_perms(PERM_ROOT, 0); + prompt = sudo_opieprompt(&opie, prompt); + set_perms(PERM_USER, 0); +#endif /* HAVE_OPIE */ + + /* get a password from the user */ +#ifdef USE_GETPASS +# ifdef HAVE_KERB4 + (void) des_read_pw_string(kpass, sizeof(kpass) - 1, prompt, 0); + pass = kpass; +# else + pass = (char *) getpass(prompt); +# endif /* HAVE_KERB4 */ +#else + pass = tgetpass(prompt, PASSWORD_TIMEOUT * 60, user_name, shost); +#endif /* USE_GETPASS */ + + /* Exit loop on nil password */ + if (!pass || *pass == '\0') { + if (counter == TRIES_FOR_PASSWORD) + exit(0); + else + break; + } + +#ifdef HAVE_SKEY + /* Only check s/key db if the user exists there */ + if (skey.keyfile) { + set_perms(PERM_ROOT, 0); + if (skeyverify(&skey, pass) == 0) { + set_perms(PERM_USER, 0); + return; /* if the key is correct return() */ + } + set_perms(PERM_USER, 0); + } +#endif /* HAVE_SKEY */ +#ifdef HAVE_OPIE + /* Only check OPIE db if the user exists there */ + if (opie.opie_flags) { + set_perms(PERM_ROOT, 0); + if (opieverify(&opie, pass) == 0) { + set_perms(PERM_USER, 0); + return; /* if the key is correct return() */ + } + set_perms(PERM_USER, 0); + } +#endif /* HAVE_OPIE */ +#if !defined(HAVE_SKEY) || !defined(SKEY_ONLY) + /* + * If we use shadow passwords with a different crypt(3) + * check that here, else use standard crypt(3). + */ +# if (SHADOW_TYPE != SPW_NONE) && (SHADOW_TYPE != SPW_BSD) +# if (SHADOW_TYPE == SPW_ULTRIX4) + if (!strcmp(user_passwd, (char *) crypt16(pass, user_passwd))) + return; /* if the passwd is correct return() */ +# endif /* ULTRIX4 */ +# if (SHADOW_TYPE == SPW_SECUREWARE) && !defined(__alpha) +# ifdef HAVE_BIGCRYPT + if (strcmp(user_passwd, (char *) bigcrypt(pass, user_passwd)) == 0) + return; /* if the passwd is correct return() */ +# else + if (strcmp(user_passwd, crypt(pass, user_passwd)) == 0) + return; /* if the passwd is correct return() */ +# endif /* HAVE_BIGCRYPT */ +# endif /* SECUREWARE && !__alpha */ +# if (SHADOW_TYPE == SPW_SECUREWARE) && defined(__alpha) + if (crypt_type == AUTH_CRYPT_BIGCRYPT) { + if (!strcmp(user_passwd, bigcrypt(pass, user_passwd))) + return; /* if the passwd is correct return() */ + } else if (crypt_type == AUTH_CRYPT_CRYPT16) { + if (!strcmp(user_passwd, crypt16(pass, user_passwd))) + return; /* if the passwd is correct return() */ +#ifdef AUTH_CRYPT_OLDCRYPT + } else if (crypt_type == AUTH_CRYPT_OLDCRYPT || + crypt_type == AUTH_CRYPT_C1CRYPT) { + if (!strcmp(user_passwd, crypt(pass, user_passwd))) + return; /* if the passwd is correct return() */ +#endif + } else { + (void) fprintf(stderr, + "%s: Sorry, I don't know how to deal with crypt type %d.\n", + Argv[0], crypt_type); + exit(1); + } +# endif /* SECUREWARE && __alpha */ +# endif /* SHADOW_TYPE != SPW_NONE && SHADOW_TYPE != SPW_BSD */ + + /* Normal UN*X password check */ + if (!strcmp(user_passwd, (char *) crypt(pass, user_passwd))) + return; /* if the passwd is correct return() */ + +# ifdef HAVE_KERB4 + if (user_uid && sudo_krb_validate_user(user_pw_ent, pass) == 0) + return; +# endif /* HAVE_KERB4 */ + +# ifdef HAVE_AFS + if (ka_UserAuthenticateGeneral(KA_USERAUTH_VERSION, + user_name, /* name */ + NULL, /* instance */ + NULL, /* realm */ + pass, /* password */ + 0, /* lifetime */ + 0, 0, /* spare */ + NULL) == 0) /* reason */ + return; +# endif /* HAVE_AFS */ +# ifdef HAVE_DCE + /* + * consult the DCE registry for password validation + * note that dce_pwent trashes pass upon return... + */ + if (dce_pwent(user_name, pass)) + return; +# endif /* HAVE_DCE */ +#endif /* !HAVE_SKEY || !SKEY_ONLY */ + + --counter; /* otherwise, try again */ +#ifdef USE_INSULTS + (void) fprintf(stderr, "%s\n", INSULT); +#else + (void) fprintf(stderr, "%s\n", INCORRECT_PASSWORD); +#endif /* USE_INSULTS */ + } + + if (counter > 0) { + log_error(PASSWORD_NOT_CORRECT); + inform_user(PASSWORD_NOT_CORRECT); + } else { + log_error(PASSWORDS_NOT_CORRECT); + inform_user(PASSWORDS_NOT_CORRECT); + } + + exit(1); +} +#endif /* HAVE_SECURID */ + + +#ifdef HAVE_KERB4 +/******************************************************************** + * + * sudo_krb_validate_user() + * + * Validate a user via kerberos. + */ +static int sudo_krb_validate_user(pw_ent, pass) + struct passwd *pw_ent; + char *pass; +{ + char realm[REALM_SZ]; + char tkfile[sizeof(_PATH_SUDO_TIMEDIR) + 4 + MAX_UID_T_LEN]; + int k_errno; + + /* Get the local realm */ + if (krb_get_lrealm(realm, 1) != KSUCCESS) + (void) fprintf(stderr, "Warning: Unable to get local kerberos realm\n"); + + /* + * Set the ticket file to be in sudo sudo timedir so we don't + * wipe out other kerberos tickets. + */ + (void) sprintf(tkfile, "%s/tkt%ld", _PATH_SUDO_TIMEDIR, + (long) pw_ent->pw_uid); + (void) krb_set_tkt_string(tkfile); + + /* + * Update the ticket if password is ok. Kerb4 expects + * the ruid and euid to be the same here so we setuid to root. + */ + set_perms(PERM_ROOT, 0); + k_errno = krb_get_pw_in_tkt(pw_ent->pw_name, "", realm, "krbtgt", realm, + DEFAULT_TKT_LIFE, pass); + + /* + * If we authenticated, destroy the ticket now that we are done with it. + * If not, warn on a "real" error. + */ + if (k_errno == INTK_OK) + dest_tkt(); + else if (k_errno != INTK_BADPW && k_errno != KDC_PR_UNKNOWN) + (void) fprintf(stderr, "Warning: Kerberos error: %s\n", + krb_err_txt[k_errno]); + + /* done with rootly stuff */ + set_perms(PERM_USER, 0); + + return(!(k_errno == INTK_OK)); +} +#endif /* HAVE_KERB4 */ + + +#ifdef HAVE_SKEY +/******************************************************************** + * + * sudo_skeyprompt() + * + * This function rewrites and return the prompt based the + * s/key challenge * and fills in the user's skey structure. + */ + +static char *sudo_skeyprompt(user_skey, p) + struct skey *user_skey; + char *p; +{ + char challenge[256]; + int rval; + static char *orig_prompt = NULL, *new_prompt = NULL; + static int op_len, np_size; + + /* save the original prompt */ + if (orig_prompt == NULL) { + orig_prompt = p; + op_len = strlen(p); + + /* ignore trailing colon */ + if (p[op_len - 1] == ':') + op_len--; + } + + /* close old stream */ + if (user_skey->keyfile) + (void) fclose(user_skey->keyfile); + + /* get the skey part of the prompt */ + if ((rval = skeychallenge(user_skey, user_name, challenge)) != 0) { +#ifdef OTP_ONLY + (void) fprintf(stderr, + "%s: You do not exist in the s/key database.\n", + Argv[0]); + exit(1); +#else + /* return the original prompt if we cannot get s/key info */ + return(orig_prompt); +#endif /* OTP_ONLY */ + } + + /* get space for new prompt with embedded s/key challenge */ + if (new_prompt == NULL) { + /* allocate space for new prompt */ + np_size = op_len + strlen(challenge) + 7; + if (!(new_prompt = (char *) malloc(np_size))) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + } else { + /* already have space allocated, is it enough? */ + if (np_size < op_len + strlen(challenge) + 7) { + np_size = op_len + strlen(challenge) + 7; + if (!(new_prompt = (char *) realloc(new_prompt, np_size))) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", + Argv[0]); + exit(1); + } + } + } + + /* embed the s/key challenge into the new password prompt */ +#ifdef LONG_OTP_PROMPT + (void) sprintf(new_prompt, "%s\n%s", challenge, orig_prompt); +#else + (void) sprintf(new_prompt, "%.*s [ %s ]:", op_len, orig_prompt, challenge); +#endif /* LONG_OTP_PROMPT */ + + return(new_prompt); +} +#endif /* HAVE_SKEY */ + + +#ifdef HAVE_OPIE +/******************************************************************** + * + * sudo_opieprompt() + * + * This function rewrites and return the prompt based the + * OPIE challenge * and fills in the user's opie structure. + */ + +static char *sudo_opieprompt(user_opie, p) + struct opie *user_opie; + char *p; +{ + char challenge[OPIE_CHALLENGE_MAX]; + int rval; + static char *orig_prompt = NULL, *new_prompt = NULL; + static int op_len, np_size; + + /* save the original prompt */ + if (orig_prompt == NULL) { + orig_prompt = p; + op_len = strlen(p); + + /* ignore trailing colon */ + if (p[op_len - 1] == ':') + op_len--; + } + + /* get the opie part of the prompt */ + if ((rval = opiechallenge(user_opie, user_name, challenge)) != 0) { +#ifdef OTP_ONLY + (void) fprintf(stderr, + "%s: You do not exist in the s/key database.\n", + Argv[0]); + exit(1); +#else + /* return the original prompt if we cannot get s/key info */ + return(orig_prompt); +#endif /* OTP_ONLY */ + } + + /* get space for new prompt with embedded s/key challenge */ + if (new_prompt == NULL) { + /* allocate space for new prompt */ + np_size = op_len + strlen(challenge) + 7; + if (!(new_prompt = (char *) malloc(np_size))) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + } else { + /* already have space allocated, is it enough? */ + if (np_size < op_len + strlen(challenge) + 7) { + np_size = op_len + strlen(challenge) + 7; + if (!(new_prompt = (char *) realloc(new_prompt, np_size))) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", + Argv[0]); + exit(1); + } + } + } + + /* embed the s/key challenge into the new password prompt */ +#ifdef LONG_OTP_PROMPT + (void) sprintf(new_prompt, "%s\n%s", challenge, orig_prompt); +#else + (void) sprintf(new_prompt, "%.*s [ %s ]:", op_len, orig_prompt, challenge); +#endif /* LONG_OTP_PROMPT */ + + return(new_prompt); +} +#endif /* HAVE_OPIE */ + + +#ifndef NO_MESSAGE +/******************************************************************** + * + * reminder() + * + * this function just prints the the reminder message + */ + +static void reminder() +{ +#ifdef SHORT_MESSAGE + (void) fprintf(stderr, "\n%s\n%s\n\n%s\n%s\n\n", +#else + (void) fprintf(stderr, "\n%s\n%s\n%s\n%s\n\n%s\n%s\n\n%s\n%s\n\n", + " CU sudo version 1.5.2, based on Root Group sudo version 1.1", + " sudo version 1.1, Copyright (C) 1991 The Root Group, Inc.", + " sudo comes with ABSOLUTELY NO WARRANTY. This is free software,", + " and you are welcome to redistribute it under certain conditions.", +#endif + "We trust you have received the usual lecture from the local System", + "Administrator. It usually boils down to these two things:", + " #1) Respect the privacy of others.", + " #2) Think before you type." + ); + + (void) fflush(stderr); +} +#endif /* NO_MESSAGE */ diff --git a/gnu/usr.bin/sudo/sudo/compat.h b/gnu/usr.bin/sudo/sudo/compat.h new file mode 100644 index 00000000000..d2ae270ab64 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/compat.h @@ -0,0 +1,145 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: compat.h,v 1.1 1996/10/14 05:14:44 millert Exp $ + */ + +#ifndef _SUDO_COMPAT_H +#define _SUDO_COMPAT_H + +/* + * Macros that may be missing on some Operating Systems + */ + +/* Deal with ansi stuff reasonably. */ +#ifndef __P +# if defined (__cplusplus) || defined (__STDC__) +# define __P(args) args +# else +# define __P(args) () +# endif +#endif /* __P */ + +/* + * Some systems (ie ISC V/386) do not define MAXPATHLEN even in param.h + */ +#ifndef MAXPATHLEN +# define MAXPATHLEN 1024 +#endif + +/* + * Some systems do not define MAXHOSTNAMELEN. + */ +#ifndef MAXHOSTNAMELEN +# define MAXHOSTNAMELEN 64 +#endif + +/* + * 4.2BSD lacks FD_* macros (we only use FD_SET and FD_ZERO) + */ +#ifndef FD_SETSIZE +#define FD_SET(fd, fds) ((fds) -> fds_bits[0] |= (1 << (fd))) +#define FD_ZERO(fds) ((fds) -> fds_bits[0] = 0) +#endif /* !FD_SETSIZE */ + +/* + * Posix versions for those without... + */ +#ifndef _S_IFMT +# define _S_IFMT S_IFMT +#endif /* _S_IFMT */ +#ifndef _S_IFREG +# define _S_IFREG S_IFREG +#endif /* _S_IFREG */ +#ifndef _S_IFDIR +# define _S_IFDIR S_IFDIR +#endif /* _S_IFDIR */ +#ifndef S_ISREG +# define S_ISREG(m) (((m) & _S_IFMT) == _S_IFREG) +#endif /* S_ISREG */ +#ifndef S_ISDIR +# define S_ISDIR(m) (((m) & _S_IFMT) == _S_IFDIR) +#endif /* S_ISDIR */ + +/* + * Some OS's may not have this. + */ +#ifndef S_IRWXU +# define S_IRWXU 0000700 /* rwx for owner */ +#endif /* S_IRWXU */ + +/* + * We need to know how long the longest password may be. + * For alternate password schemes we need longer passwords. + * This is a bit, ummm, gross but necesary. + */ +#if defined(HAVE_KERB4) || defined(HAVE_AFS) || defined(HAVE_DCE) || defined(HAVE_SKEY) || defined(HAVE_OPIE) +# undef _PASSWD_LEN +# define _PASSWD_LEN 256 +#else +# if (SHADOW_TYPE == SPW_SECUREWARE) +# undef _PASSWD_LEN +# define _PASSWD_LEN AUTH_MAX_PASSWD_LENGTH +# else +# ifndef _PASSWD_LEN +# ifdef PASS_MAX +# define _PASSWD_LEN PASS_MAX +# else +# if (SHADOW_TYPE != SPW_NONE) +# define _PASSWD_LEN 24 +# else +# define _PASSWD_LEN 8 +# endif /* SHADOW_TYPE != SPW_NONE */ +# endif /* PASS_MAX */ +# endif /* !_PASSWD_LEN */ +# endif /* HAVE_KERB4 || HAVE_AFS || HAVE_DCE || HAVE_SKEY || HAVE_OPIE */ +#endif /* SPW_SECUREWARE */ + +/* + * Some OS's lack these + */ +#ifndef UID_NO_CHANGE +# define UID_NO_CHANGE ((uid_t) -1) +#endif /* UID_NO_CHANGE */ +#ifndef GID_NO_CHANGE +# define GID_NO_CHANGE ((gid_t) -1) +#endif /* GID_NO_CHANGE */ + +/* + * Emulate seteuid() for AIX via setuidx() -- needed for some versions of AIX + */ +#ifdef _AIX +# include <sys/id.h> +# define seteuid(_EUID) (setuidx(ID_EFFECTIVE|ID_REAL, _EUID)) +# undef HAVE_SETEUID +# define HAVE_SETEUID 1 +#endif /* _AIX */ + +/* + * Emulate seteuid() for HP-UX via setresuid(2) and seteuid(2) for others. + */ +#ifndef HAVE_SETEUID +# ifdef __hpux +# define seteuid(_EUID) (setresuid(UID_NO_CHANGE, _EUID, UID_NO_CHANGE)) +# else +# define seteuid(_EUID) (setresuid(UID_NO_CHANGE, _EUID)) +# endif /* __hpux */ +#endif /* HAVE_SETEUID */ + +#endif /* _SUDO_COMPAT_H */ diff --git a/gnu/usr.bin/sudo/sudo/config.h b/gnu/usr.bin/sudo/sudo/config.h new file mode 100644 index 00000000000..a9dc67a9ce3 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/config.h @@ -0,0 +1,304 @@ +/* config.h. Generated automatically by configure. */ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: config.h,v 1.1 1996/10/14 05:14:44 millert Exp $ + */ + +/* + * config.h -- You shouldn't edit this by hand unless you are + * NOT using configure. + */ + +/* New ANSI-style OS defs. */ +#if defined(hpux) && !defined(__hpux) +# define __hpux 1 +#endif /* hpux */ + +#if defined(convex) && !defined(__convex__) +# define __convex__ 1 +#endif /* convex */ + +/* Define if on AIX 3. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _ALL_SOURCE +/* #undef _ALL_SOURCE */ +#endif + +/* Define if on ConvexOs. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _CONVEX_SOURCE +/* #undef _CONVEX_SOURCE */ +#endif + +/* Define if needed to get POSIX functionality. + System headers sometimes define this. + We just want to avoid a redefinition error message. */ +#ifndef _POSIX_SOURCE +/* #undef _POSIX_SOURCE */ +#endif + +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef uid_t */ + +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef gid_t */ + +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef mode_t */ + +/* Define to `unsigned' if <sys/types.h> doesn't define. */ +/* #undef size_t */ + +/* Define to `int' if <sys/types.h> doesn't define. */ +/* #undef ssize_t */ + +/* Define to be nil if C compiler doesn't support "const." */ +/* #undef const */ + +/* Define as the return type of signal handlers (int or void). */ +#define RETSIGTYPE void + +/* Define if you have the ANSI C header files. */ +#define STDC_HEADERS 1 + +/* Define if you want to use the system getpass(). */ +/* #undef USE_GETPASS */ + +/* Define if you use S/Key. */ +/* #undef HAVE_SKEY */ + +/* Define if you use NRL OPIE. */ +/* #undef HAVE_OPIE */ + +/* Define if you use SecurID. */ +/* #undef HAVE_SECURID */ + +/* Define if you use Kerberos. */ +/* #undef HAVE_KERB4 */ + +/* Define if you use Kerberos. */ +/* #undef HAVE_KERB5 */ + +/* Keberos v5 has v4 compatibility */ +#ifdef HAVE_KERB5 +# define HAVE_KERB4 +#endif /* HAVE_KERB5 */ + +/* Define if you use AFS. */ +/* #undef HAVE_AFS */ + +/* Define if you use OSF DCE. */ +/* #undef HAVE_DCE */ + +/* Define if you have POSIX signals. */ +#define HAVE_SIGACTION 1 +#ifdef HAVE_SIGACTION +# define POSIX_SIGNALS +#endif /* HAVE_SIGACTION */ + +/* Define if you have tzset(3). */ +#define HAVE_TZSET 1 + +/* Define if you have getcwd(3). */ +/* #undef HAVE_GETCWD */ + +/* Define if you have getwd(3). */ +#define HAVE_GETWD 1 + +/* Define if you have strdup(3). */ +#define HAVE_STRDUP 1 + +/* Define if you have fnmatch(3). */ +#define HAVE_FNMATCH 1 + +/* Define if you have lsearch(3). */ +#define HAVE_LSEARCH 1 + +/* Define if you have strchr(3). */ +#define HAVE_STRCHR 1 +#if !defined(HAVE_STRCHR) && !defined(strchr) +# define strchr index +#endif + +/* Define if you have strrchr(3). */ +#define HAVE_STRRCHR 1 +#if !defined(HAVE_STRRCHR) && !defined(strrchr) +# define strrchr rindex +#endif + +/* Define if you have memcpy(3). */ +#define HAVE_MEMCPY 1 +#if !defined(HAVE_MEMCPY) && !defined(memcpy) +# define memcpy(D, S, L) (bcopy(S, D, L)) +#endif + +/* Define if you have memset(3). */ +#define HAVE_MEMSET 1 +#if !defined(HAVE_MEMSET) && !defined(memset) +# define memset(S, X, N) (bzero(S, N)) +#endif + +/* Define if you have sysconf(3c). */ +#define HAVE_SYSCONF 1 + +/* Define if you have putenv(3). */ +/* #undef HAVE_PUTENV */ + +/* Define if you have setenv(3). */ +#define HAVE_SETENV 1 + +/* Define if you have strcasecmp(3). */ +#define HAVE_STRCASECMP 1 + +/* Define if you have tcgetattr(3). */ +#define HAVE_TCGETATTR 1 + +/* Define if you have innetgr(3). */ +#define HAVE_INNETGR 1 + +/* Define if you have getdomainname(2). */ +#define HAVE_GETDOMAINNAME 1 + +/* Define if you have utime(2). */ +#define HAVE_UTIME 1 + +/* Define if you have a POSIX utime() (uses struct utimbuf) */ +#define HAVE_UTIME_POSIX 1 + +/* Define if utime(file, NULL) sets timestamp to current */ +#define HAVE_UTIME_NULL 1 + +/* Define if you have bigcrypt(3). */ +/* #undef HAVE_BIGCRYPT */ + +/* Define if you have seteuid(3). */ +#define HAVE_SETEUID 1 + +/* Define if you have the <malloc.h> header file. */ +#define HAVE_MALLOC_H 1 + +/* Define if you have the <alloca.h> header file. */ +/* #undef HAVE_ALLOCA_H */ + +/* Define if you have the <paths.h> header file. */ +#define HAVE_PATHS_H 1 + +/* Define if you have the <string.h> header file. */ +#define HAVE_STRING_H 1 + +/* Define if you have the <strings.h> header file. */ +#if !defined(__convex__) && !defined(convex) +#define HAVE_STRINGS_H 1 +#endif /* convex */ + +/* Define your flavor of dir entry header file. */ +#define HAVE_DIRENT_H 1 +/* #undef HAVE_SYS_NDIR_H */ +/* #undef HAVE_SYS_DIR_H */ +/* #undef HAVE_NDIR_H */ + +/* Define if you have the <utime.h> header file. */ +#define HAVE_UTIME_H 1 + +/* Define if you have the <unistd.h> header file. */ +#define HAVE_UNISTD_H 1 + +/* Define if you have the <fnmatch.h> header file. */ +#define HAVE_FNMATCH_H 1 + +/* Define if you have the <netgroup.h> header file. */ +#define HAVE_NETGROUP_H 1 + +/* Define if you have the <termio.h> header file. */ +/* #undef HAVE_TERMIO_H */ + +/* Define if you have the <termios.h> header file and tcgetattr(3). */ +#ifdef HAVE_TCGETATTR +#define HAVE_TERMIOS_H 1 +#endif /* HAVE_TCGETATTR */ + +/* Define if you have the <sys/sockio.h> header file. */ +#define HAVE_SYS_SOCKIO_H 1 + +/* Define if you have the <sys/bsdtypes.h> header file. */ +/* #undef HAVE_SYS_BSDTYPES_H */ + +/* Define if you have the <sys/select.h> header file. */ +#define HAVE_SYS_SELECT_H 1 + +/* Define if your struct sockadr has an sa_len field. */ +#define HAVE_SA_LEN 1 + +/* Supported shadow password types */ +#define SPW_NONE 0x00 +#define SPW_SECUREWARE 0x01 +#define SPW_HPUX9 0x02 +#define SPW_SUNOS4 0x03 +#define SPW_SVR4 0x04 +#define SPW_ULTRIX4 0x05 +#define SPW_BSD 0x06 + +/* Define to the variety of shadow passwords supported on your OS */ +#define SHADOW_TYPE SPW_BSD + +/* Define to void if your C compiler fully groks void, else char */ +#define VOID void + +/* Define to the max length of a uid_t in string context (excluding the NULL */ +#define MAX_UID_T_LEN 10 + +/* Define if your syslog(3) does not guarantee the message will be logged */ +/* and syslog(3) returns non-zero to denote failure */ +/* #undef BROKEN_SYSLOG */ + +/* + * Paths to commands used by sudo. There are used by pathnames.h. + * If you want to override these values, do so in pathnames.h, not here! + */ + +#ifndef _CONFIG_PATH_SENDMAIL +#define _CONFIG_PATH_SENDMAIL "/usr/sbin/sendmail" +#endif /* _CONFIG_PATH_SENDMAIL */ + +#ifndef _CONFIG_PATH_VI +#define _CONFIG_PATH_VI "/usr/bin/vi" +#endif /* _CONFIG_PATH_VI */ + +#ifndef _CONFIG_PATH_PWD +#define _CONFIG_PATH_PWD "/bin/pwd" +#endif /* _CONFIG_PATH_PWD */ + +#ifndef _CONFIG_PATH_MV +#define _CONFIG_PATH_MV "/bin/mv" +#endif /* _CONFIG_PATH_MV */ + +#ifndef _CONFIG_PATH_BSHELL +#define _CONFIG_PATH_BSHELL "/bin/sh" +#endif /* _CONFIG_PATH_BSHELL */ + +#ifndef _CONFIG_PATH_LOGFILE +#define _CONFIG_PATH_LOGFILE "/var/log/sudo.log" +#endif /* _CONFIG_PATH_LOGFILE */ + +#ifndef _CONFIG_PATH_TIMEDIR +#define _CONFIG_PATH_TIMEDIR "/var/run/sudo" +#endif /* _CONFIG_PATH_TIMEDIR */ diff --git a/gnu/usr.bin/sudo/sudo/find_path.c b/gnu/usr.bin/sudo/sudo/find_path.c new file mode 100644 index 00000000000..e0b0b6aa512 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/find_path.c @@ -0,0 +1,187 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * This module contains the find_path() function that returns + * TRUE if the command was found and FALSE if not. + * If find_path() returns TRUE, the copyin paramters command and + * ocommand contain the resolved and unresolved pathnames respectively. + * NOTE: if "." or "" exists in PATH it will be searched last. + * + * Todd C. Miller (millert@colorado.edu) Sat Mar 25 21:50:36 MST 1995 + */ + +#ifndef lint +static char rcsid[] = "$Id: find_path.c,v 1.1 1996/10/14 05:14:45 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#include <errno.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <netinet/in.h> +#include "sudo.h" +#include <options.h> + +#ifndef STDC_HEADERS +#ifndef __GNUC__ /* gcc has its own malloc */ +extern char *malloc __P((size_t)); +#endif /* __GNUC__ */ +extern char *getenv __P((const char *)); +extern char *strcpy __P((char *, const char *)); +extern int fprintf __P((FILE *, const char *, ...)); +extern ssize_t readlink __P((const char *, VOID *, size_t)); +extern int stat __P((const char *, struct stat *)); +extern int lstat __P((const char *, struct stat *)); +#ifdef HAVE_STRDUP +extern char *strdup __P((const char *)); +#endif /* HAVE_STRDUP */ +#endif /* !STDC_HEADERS */ + + +#ifndef _S_IFMT +#define _S_IFMT S_IFMT +#endif /* _S_IFMT */ +#ifndef _S_IFLNK +#define _S_IFLNK S_IFLNK +#endif /* _S_IFLNK */ + + +/******************************************************************* + * + * find_path() + * + * this function finds the full pathname for a command and + * stores it in a statically allocated array, returning a pointer + * to the array. + */ + +char * find_path(file) + char *file; /* file to find */ +{ + static char command[MAXPATHLEN + 1]; /* qualified filename */ + register char *n; /* for traversing path */ + char *path = NULL; /* contents of PATH env var */ + char *origpath; /* so we can free path later */ + char *result = NULL; /* result of path/file lookup */ +#ifndef IGNORE_DOT_PATH + int checkdot = 0; /* check current dir? */ +#endif /* IGNORE_DOT_PATH */ + + command[0] = '\0'; + + if (strlen(file) > MAXPATHLEN) { + errno = ENAMETOOLONG; + (void) fprintf(stderr, "%s: path too long: %s\n", Argv[0], file); + exit(1); + } + + /* + * If we were given a fully qualified or relative path + * there is no need to look at PATH. + * We really want to fall back if !sudo_goodpath() but then + * the error is "not found" -- this way we get the correct error. + */ + if (strchr(file, '/')) { + (void) strcpy(command, file); + if (sudo_goodpath(command)) { + return(command); + } else { + (void) fprintf(stderr, "%s: %s: ", Argv[0], command); + perror(""); + exit(1); + } + } + + /* + * grab PATH out of environment and make a local copy + */ + if ((path = getenv("PATH")) == NULL) + return(NULL); + + if ((path = (char *) strdup(path)) == NULL) { + (void) fprintf(stderr, "%s: out of memory!\n", Argv[0]); + exit(1); + } + origpath=path; + + /* XXX use strtok() */ + do { + if ((n = strchr(path, ':'))) + *n = '\0'; + + /* + * search current dir last if it is in PATH This will miss sneaky + * things like using './' or './/' + */ + if (*path == '\0' || (*path == '.' && *(path + 1) == '\0')) { +#ifndef IGNORE_DOT_PATH + checkdot = 1; +#endif /* IGNORE_DOT_PATH */ + path = n + 1; + continue; + } + + /* + * resolve the path and exit the loop if found + */ + if (strlen(path) + strlen(file) >= MAXPATHLEN) { + errno = ENAMETOOLONG; + (void) fprintf(stderr, "%s: path too long: %s\n", Argv[0], file); + exit(1); + } + (void) sprintf(command, "%s/%s", path, file); + if ((result = sudo_goodpath(command))) + break; + + path = n + 1; + + } while (n); + +#ifndef IGNORE_DOT_PATH + /* + * check current dir if dot was in the PATH + */ + if (!result && checkdot) + result = sudo_goodpath(file); +#endif /* IGNORE_DOT_PATH */ + + (void) free(origpath); + + return(result); +} diff --git a/gnu/usr.bin/sudo/sudo/getspwuid.c b/gnu/usr.bin/sudo/sudo/getspwuid.c new file mode 100644 index 00000000000..7ff4a16185c --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/getspwuid.c @@ -0,0 +1,266 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * This module contains sudo_getpwuid(), a function that + * Makes a dynamic copy of the struct passwd returned by + * getpwuid() and substitutes the shadow password if + * necesary. + * + * Todd C. Miller Mon Nov 20 13:53:06 MST 1995 + */ + +#ifndef lint +static char rcsid[] = "$Id: getspwuid.c,v 1.1 1996/10/14 05:14:46 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <pwd.h> +#include "sudo.h" +#include <options.h> +#if (SHADOW_TYPE != SPW_NONE) && (SHADOW_TYPE != SPW_BSD) +# if (SHADOW_TYPE == SPW_SVR4) +# include <shadow.h> +# endif /* SVR4 */ +# if (SHADOW_TYPE == SPW_SECUREWARE) +# ifdef __hpux +# include <hpsecurity.h> +# else +# include <sys/security.h> +# endif /* __hpux */ +# include <prot.h> +# endif /* SECUREWARE */ +# if (SHADOW_TYPE == SPW_ULTRIX4) +# include <auth.h> +# endif /* ULTRIX4 */ +# if (SHADOW_TYPE == SPW_SUNOS4) +# include <sys/label.h> +# include <sys/audit.h> +# include <pwdadj.h> +# endif /* SUNOS4 */ +#endif /* SHADOW_TYPE != SPW_NONE && SHADOW_TYPE != SPW_BSD */ + +#ifndef STDC_HEADERS +#ifndef __GNUC__ /* gcc has its own malloc */ +extern char *malloc __P((size_t)); +#endif /* __GNUC__ */ +extern char *getenv __P((const char *)); +#ifdef HAVE_STRDUP +extern char *strdup __P((const char *)); +#endif /* HAVE_STRDUP */ +#endif /* !STDC_HEADERS */ + +/* + * Global variables (yuck) + */ +#if (SHADOW_TYPE == SPW_SECUREWARE) && defined(__alpha) +uchar_t crypt_type; +#endif /* SPW_SECUREWARE && __alpha */ + + +/* + * Local functions not visible outside getspwuid.c + */ +static char *sudo_getshell __P((struct passwd *)); +static char *sudo_getspwd __P((struct passwd *)); + + + +/********************************************************************** + * + * sudo_getshell() + * + * This function returns the user's shell based on either the + * SHELL evariable or the passwd(5) entry (in that order). + */ + +static char *sudo_getshell(pw_ent) + struct passwd *pw_ent; +{ + char *pw_shell; + + if ((pw_shell = getenv("SHELL")) == NULL) + pw_shell = pw_ent -> pw_shell; + +#ifdef _PATH_BSHELL + /* empty string "" means bourne shell */ + if (*pw_shell == '\0') + pw_shell = _PATH_BSHELL; +#endif /* _PATH_BSHELL */ + + return(pw_shell); +} + + +/********************************************************************** + * + * sudo_getspwd() + * + * This function returns the shadow password for the user described + * by pw_ent. If there is no shadow password the normal UN*X password + * is returned instead. + */ + +static char *sudo_getspwd(pw_ent) + struct passwd *pw_ent; +#if (SHADOW_TYPE != SPW_NONE) && (SHADOW_TYPE != SPW_BSD) +# if (SHADOW_TYPE == SPW_SVR4) +{ + struct spwd *spw_ent; + + if ((spw_ent = getspnam(pw_ent -> pw_name)) && spw_ent -> sp_pwdp) + return(spw_ent -> sp_pwdp); + else + return(pw_ent -> pw_passwd); +} +# endif /* SVR4 */ +# if (SHADOW_TYPE == SPW_HPUX9) +{ + struct s_passwd *spw_ent; + + if ((spw_ent = getspwuid(pw_ent -> pw_uid)) && spw_ent -> pw_passwd) + return(spw_ent -> pw_passwd); + else + return(pw_ent -> pw_passwd); +} +# endif /* HPUX9 */ +# if (SHADOW_TYPE == SPW_SUNOS4) +{ + struct passwd_adjunct *spw_ent; + + if ((spw_ent = getpwanam(pw_ent -> pw_name)) && spw_ent -> pwa_passwd) + return(spw_ent -> pwa_passwd); + else + return(pw_ent -> pw_passwd); +} +# endif /* SUNOS4 */ +# if (SHADOW_TYPE == SPW_ULTRIX4) +{ + AUTHORIZATION *spw_ent; + + if ((spw_ent = getauthuid(pw_ent -> pw_uid)) && spw_ent -> a_password) + return(spw_ent -> a_password); + else + return(pw_ent -> pw_passwd); +} +# endif /* ULTRIX4 */ +# if (SHADOW_TYPE == SPW_SECUREWARE) +{ + struct pr_passwd *spw_ent; + + if ((spw_ent = getprpwuid(pw_ent->pw_uid)) && spw_ent->ufld.fd_encrypt) { +# ifdef __alpha + crypt_type = spw_ent -> ufld.fd_oldcrypt; +# ifdef AUTH_CRYPT_C1CRYPT + if (crypt_type == AUTH_CRYPT_C1CRYPT) + return(pw_ent -> pw_passwd); +# endif /* AUTH_CRYPT_C1CRYPT */ +# endif /* __alpha */ + return(spw_ent -> ufld.fd_encrypt); + } else + return(pw_ent -> pw_passwd); +} +# endif /* SECUREWARE */ +#else +{ + return(pw_ent->pw_passwd); +} +#endif /* SHADOW_TYPE != SPW_NONE && SHADOW_TYPE != SPW_BSD */ + + +/********************************************************************** + * + * sudo_getpwuid() + * + * This function dynamically allocates space for a struct password + * and the constituent parts that we care about. If shadow passwords + * are in use, it substitutes the shadow password for pw_passwd. + */ + +struct passwd *sudo_getpwuid(uid) + uid_t uid; +{ + struct passwd *pw_ent, *local_pw_ent; + + if ((pw_ent = getpwuid(uid)) == NULL) + return(NULL); + + /* allocate space for a local copy of pw_ent */ + local_pw_ent = (struct passwd *) malloc(sizeof(struct passwd)); + if (local_pw_ent == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* + * Copy the struct passwd and the interesting strings... + */ + (void) memcpy(local_pw_ent, pw_ent, sizeof(struct passwd)); + + local_pw_ent->pw_name = (char *) strdup(pw_ent->pw_name); + if (local_pw_ent->pw_name == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + local_pw_ent->pw_dir = (char *) strdup(pw_ent->pw_dir); + if (local_pw_ent->pw_dir == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* pw_shell is a special case since we overide with $SHELL */ + local_pw_ent->pw_shell = (char *) strdup(sudo_getshell(pw_ent)); + if (local_pw_ent->pw_shell == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* pw_passwd gets a shadow password if applicable */ + local_pw_ent->pw_passwd = (char *) strdup(sudo_getspwd(pw_ent)); + if (local_pw_ent->pw_passwd == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + return(local_pw_ent); +} diff --git a/gnu/usr.bin/sudo/sudo/goodpath.c b/gnu/usr.bin/sudo/sudo/goodpath.c new file mode 100644 index 00000000000..61a19ac84d9 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/goodpath.c @@ -0,0 +1,99 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * This module contains sudo_goodpath(3) + * + * sudo_goodpath(3) takes a path to check and returns its argument + * if the path is stat(2)'able, a regular file, and executable by + * root. The string's size should be <= MAXPATHLEN. + * + * Todd C. Miller (millert@colorado.edu) Sat Mar 25 21:58:17 MST 1995 + */ + +#ifndef lint +static char rcsid[] = "$Id: goodpath.c,v 1.1 1996/10/14 05:14:46 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#include <errno.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/param.h> +#include <netinet/in.h> + +#include "sudo.h" +#include <options.h> + +#ifndef STDC_HEADERS +extern int stat __P((const char *, struct stat *)); +#endif /* !STDC_HEADERS */ + + +/****************************************************************** + * + * sudo_goodpath() + * + * this function takes a path and makes sure it describes a a file + * that is a normal file and executable by root. + */ + +char * sudo_goodpath(path) + const char * path; +{ + struct stat statbuf; /* for stat(2) */ + int err; /* if stat(2) got an error */ + + /* check for brain damage */ + if (path == NULL || path[0] == '\0') + return(NULL); + + /* we need to be root for the stat */ + set_perms(PERM_ROOT, 0); + + err = stat(path, &statbuf); + + /* discard root perms */ + set_perms(PERM_USER, 0); + + /* stat(3) failed */ + if (err) + return(NULL); + + /* make sure path describes an executable regular file */ + if (S_ISREG(statbuf.st_mode) && (statbuf.st_mode & 0000111)) { + return((char *)path); + } else { + /* file is not executable/regular */ + errno = EACCES; + return(NULL); + } +} diff --git a/gnu/usr.bin/sudo/sudo/ins_2001.h b/gnu/usr.bin/sudo/sudo/ins_2001.h new file mode 100644 index 00000000000..b062f17f795 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/ins_2001.h @@ -0,0 +1,39 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: ins_2001.h,v 1.1 1996/10/14 05:14:47 millert Exp $ + */ + +#ifndef _SUDO_INS_2001_H +#define _SUDO_INS_2001_H + + /* + * HAL insults (paraphrased) from 2001. + */ + + "Just what do you think you're doing Dave?", + "It can only be attributed to human error.", + "That's something I cannot allow to happen.", + "My mind is going. I can feel it.", + "Sorry about this, I know it's a bit silly.", + "Take a stress pill and think things over.", + "This mission is too important for me to allow you to jeopardize it.", + "I feel much better now.", + +#endif /* _SUDO_INS_2001_H */ diff --git a/gnu/usr.bin/sudo/sudo/ins_classic.h b/gnu/usr.bin/sudo/sudo/ins_classic.h new file mode 100644 index 00000000000..2f395bd6c29 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/ins_classic.h @@ -0,0 +1,39 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: ins_classic.h,v 1.1 1996/10/14 05:14:47 millert Exp $ + */ + +#ifndef _SUDO_INS_CLASSIC_H +#define _SUDO_INS_CLASSIC_H + + /* + * Insults from the original sudo(8). + */ + + "Wrong! You cheating scum!", + "No soap, honkie-lips.", + "Where did you learn to type?", + "Are you on drugs?", + "My pet ferret can type better than you!", + "You type like i drive.", + "Do you think like you type?", + "Your mind just hasn't been the same since the electro-shock, has it?", + +#endif /* _SUDO_INS_CLASSIC_H */ diff --git a/gnu/usr.bin/sudo/sudo/ins_csops.h b/gnu/usr.bin/sudo/sudo/ins_csops.h new file mode 100644 index 00000000000..b948e0bb447 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/ins_csops.h @@ -0,0 +1,40 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: ins_csops.h,v 1.1 1996/10/14 05:14:48 millert Exp $ + */ + +#ifndef _SUDO_INS_CSOPS_H +#define _SUDO_INS_CSOPS_H + + /* + * CSOps insults (may be site dependent). + */ + + "Maybe if you used more than just two fingers...", + "BOB says: You seem to have forgotten your passwd, enter another!", + "stty: unknown mode: doofus", + "I can't hear you -- I'm using the scrambler.", + "The more you drive -- the dumber you get.", + "Listen, burrito brains, I don't have time to listen to this trash.", + "I've seen penguins that can type better than that.", + "Have you considered trying to match wits with a rutabaga?", + "You speak an infinite deal of nothing", + +#endif /* _SUDO_INS_CSOPS_H */ diff --git a/gnu/usr.bin/sudo/sudo/ins_goons.h b/gnu/usr.bin/sudo/sudo/ins_goons.h new file mode 100644 index 00000000000..3fade04a3d1 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/ins_goons.h @@ -0,0 +1,54 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: ins_goons.h,v 1.1 1996/10/14 05:14:48 millert Exp $ + */ + +#ifndef _SUDO_INS_GOONS_H +#define _SUDO_INS_GOONS_H + + /* + * Insults from the "Goon Show." + */ + + "You silly, twisted boy you.", + "He has fallen in the water!", + "We'll all be murdered in our beds!", + "You can't come in. Our tiger has got flu", + "I don't wish to know that.", + "What, what, what, what, what, what, what, what, what, what?", + "You can't get the wood, you know.", + "You'll starve!", + "... and it used to be so popular...", + "Pauses for audience applause, not a sausage", + "Hold it up to the light --- not a brain in sight!", + "Have a gorilla...", + "There must be cure for it!", + "There's a lot of it about, you know.", + "You do that again and see what happens...", + "Ying Tong Iddle I Po", + "Harm can come to a young lad like that!", + "And with that remarks folks, the case of the Crown vs yourself was proven.", + "Speak English you fool --- there are no subtitles in this scene.", + "You gotta go owwwww!", + "I have been called worse.", + "It's only your word against mine.", + "I think ... err ... I think ... I think I'll go home", + +#endif /* _SUDO_INS_GOONS_H */ diff --git a/gnu/usr.bin/sudo/sudo/insults.h b/gnu/usr.bin/sudo/sudo/insults.h new file mode 100644 index 00000000000..013d05b50ba --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/insults.h @@ -0,0 +1,71 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: insults.h,v 1.1 1996/10/14 05:14:49 millert Exp $ + */ + +#ifndef _SUDO_INSULTS_H +#define _SUDO_INSULTS_H + +#ifdef USE_INSULTS + +#if !defined(HAL_INSULTS) && !defined(GOONS_INSULTS) && !defined(CLASSIC_INSULTS) +# define CLASSIC_INSULTS +# define CSOPS_INSULTS +#endif + +/* + * Use one or more set of insults as defined in options.h. + */ + +char *insults[] = { + +# ifdef HAL_INSULTS +# include "ins_2001.h" +# endif + +# ifdef GOONS_INSULTS +# include "ins_goons.h" +# endif + +# ifdef CLASSIC_INSULTS +# include "ins_classic.h" +# endif + +# ifdef CSOPS_INSULTS +# include "ins_csops.h" +# endif + + (char *) 0 + +}; + +/* + * How may I insult you? Let me count the ways... + */ +#define NOFINSULTS (sizeof(insults) / sizeof(insults[0]) - 1) + +/* + * return a pseudo-random insult. + */ +#define INSULT (insults[time(NULL) % NOFINSULTS]) + +#endif /* USE_INSULTS */ + +#endif /* _SUDO_INSULTS_H */ diff --git a/gnu/usr.bin/sudo/sudo/interfaces.c b/gnu/usr.bin/sudo/sudo/interfaces.c new file mode 100644 index 00000000000..a2a12773150 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/interfaces.c @@ -0,0 +1,264 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * This module contains load_interfaces() a function that + * fills the interfaces global with a list of active ip + * addresses and their associated netmasks. + * + * Todd C. Miller Mon May 1 20:48:43 MDT 1995 + */ + +#ifndef lint +static char rcsid[] = "$Id: interfaces.c,v 1.1 1996/10/14 05:14:49 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#include <netdb.h> +#include <errno.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <sys/param.h> +#ifdef HAVE_SYS_SOCKIO_H +#include <sys/sockio.h> +#else +#include <sys/ioctl.h> +#endif /* HAVE_SYS_SOCKIO_H */ +#ifdef _ISC +#include <sys/stream.h> +#include <sys/sioctl.h> +#include <sys/stropts.h> +#include <net/errno.h> +#define STRSET(cmd, param, len) {strioctl.ic_cmd=(cmd);\ + strioctl.ic_dp=(param);\ + strioctl.ic_timout=0;\ + strioctl.ic_len=(len);} +#endif /* _ISC */ +#ifdef _MIPS +#include <net/soioctl.h> +#endif /* _MIPS */ +#include <netinet/in.h> +#include <arpa/inet.h> +#include <sys/time.h> +#include <net/if.h> + +#include "sudo.h" +#include <options.h> +#include "version.h" + +#if !defined(STDC_HEADERS) && !defined(__GNUC__) +extern char *malloc __P((size_t)); +#endif /* !STDC_HEADERS && !__GNUC__ */ + +/* + * Globals + */ +struct interface *interfaces; +int num_interfaces = 0; +extern int Argc; +extern char **Argv; + + +#if defined(SIOCGIFCONF) && !defined(STUB_LOAD_INTERFACES) +/********************************************************************** + * + * load_interfaces() + * + * This function sets the interfaces global variable + * and sets the constituent ip addrs and netmasks. + */ + +void load_interfaces() +{ + struct ifconf *ifconf; + char ifconf_buf[sizeof(struct ifconf) + BUFSIZ]; + struct ifreq ifreq, *ifr; + struct sockaddr_in *sin; + unsigned long localhost_mask; + int sock, n, i; +#ifdef _ISC + struct strioctl strioctl; +#endif /* _ISC */ + + /* so we can skip localhost and its ilk */ + localhost_mask = inet_addr("127.0.0.0"); + + sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) { + perror("socket"); + exit(1); + } + + /* + * get interface configuration or return (leaving interfaces NULL) + */ + ifconf = (struct ifconf *) ifconf_buf; + ifconf->ifc_buf = (caddr_t) (ifconf_buf + sizeof(struct ifconf)); + ifconf->ifc_len = sizeof(ifconf_buf) - sizeof(struct ifconf); + + /* networking may not be installed in kernel */ +#ifdef _ISC + STRSET(SIOCGIFCONF, (caddr_t) ifconf, sizeof(ifconf_buf)); + if (ioctl(sock, I_STR, (caddr_t) &strioctl) < 0) +#else + if (ioctl(sock, SIOCGIFCONF, (caddr_t) ifconf) < 0) +#endif /* _ISC */ + return; + + /* + * get the maximum number of interfaces that *could* exist. + */ + n = ifconf->ifc_len / sizeof(struct ifreq); + + /* + * malloc() space for interfaces array + */ + interfaces = (struct interface *) malloc(sizeof(struct interface) * n); + if (interfaces == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* + * for each interface, get the ip address and netmask + */ + for (ifreq.ifr_name[0] = '\0', i = 0; i < ifconf->ifc_len; ) { + /* get a pointer to the current interface */ + ifr = (struct ifreq *) ((caddr_t) ifconf->ifc_req + i); + + /* set i to the subscript of the next interface */ +#ifdef HAVE_SA_LEN + if (ifr->ifr_addr.sa_len > sizeof(ifr->ifr_addr)) + i += sizeof(ifr->ifr_name) + ifr->ifr_addr.sa_len; + else +#endif /* HAVE_SA_LEN */ + i += sizeof(struct ifreq); + + /* skip duplicates and interfaces with NULL addresses */ + sin = (struct sockaddr_in *) &ifr->ifr_addr; + if (sin->sin_addr.s_addr == 0 || + strncmp(ifr->ifr_name, ifreq.ifr_name, sizeof(ifr->ifr_name)) == 0) + continue; + + /* make a working copy... */ + ifreq = *ifr; + + /* get the ip address */ +#ifdef _ISC + STRSET(SIOCGIFADDR, (caddr_t) &ifreq, sizeof(ifreq)); + if (ioctl(sock, I_STR, (caddr_t) &strioctl) < 0) { +#else + if (ioctl(sock, SIOCGIFADDR, (caddr_t) &ifreq)) { +#endif /* _ISC */ + /* non-fatal error if interface is down or not supported */ + if (errno == EADDRNOTAVAIL || errno == ENXIO || errno == EAFNOSUPPORT) + continue; + + (void) fprintf(stderr, "%s: Error, ioctl: SIOCGIFADDR ", Argv[0]); + perror(""); + exit(1); + } + sin = (struct sockaddr_in *) &ifreq.ifr_addr; + + /* store the ip address */ + interfaces[num_interfaces].addr.s_addr = sin->sin_addr.s_addr; + + /* get the netmask */ +#ifdef SIOCGIFNETMASK +#ifdef _ISC + STRSET(SIOCGIFNETMASK, (caddr_t) &ifreq, sizeof(ifreq)); + if (ioctl(sock, I_STR, (caddr_t) &strioctl) == 0) { +#else + if (ioctl(sock, SIOCGIFNETMASK, (caddr_t) &ifreq) == 0) { +#endif /* _ISC */ + sin = (struct sockaddr_in *) &ifreq.ifr_addr; + + /* store the netmask */ + interfaces[num_interfaces].netmask.s_addr = sin->sin_addr.s_addr; + } else { +#else + { +#endif /* SIOCGIFNETMASK */ + if (IN_CLASSC(interfaces[num_interfaces].addr.s_addr)) + interfaces[num_interfaces].netmask.s_addr = htonl(IN_CLASSC_NET); + else if (IN_CLASSB(interfaces[num_interfaces].addr.s_addr)) + interfaces[num_interfaces].netmask.s_addr = htonl(IN_CLASSB_NET); + else + interfaces[num_interfaces].netmask.s_addr = htonl(IN_CLASSA_NET); + } + + /* avoid localhost and friends */ + if ((interfaces[num_interfaces].addr.s_addr & + interfaces[num_interfaces].netmask.s_addr) == localhost_mask) + continue; + + num_interfaces++; + } + + /* if there were bogus entries, realloc the array */ + if (n != num_interfaces) { + /* it is unlikely that num_interfaces will be 0 but who knows... */ + if (num_interfaces != 0) { + interfaces = (struct interface *) realloc(interfaces, + sizeof(struct interface) * num_interfaces); + if (interfaces == NULL) { + perror("realloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + } else { + (void) free(interfaces); + } + } +} + +#else /* !SIOCGIFCONF || STUB_LOAD_INTERFACES */ + +/********************************************************************** + * + * load_interfaces() + * + * Stub function for those without SIOCGIFCONF + */ + +void load_interfaces() +{ + return; +} + +#endif /* SIOCGIFCONF && !STUB_LOAD_INTERFACES */ diff --git a/gnu/usr.bin/sudo/sudo/logging.c b/gnu/usr.bin/sudo/sudo/logging.c new file mode 100644 index 00000000000..85cc707a80a --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/logging.c @@ -0,0 +1,709 @@ +/* + * CU sudo version 1.5.2 (based on Root Group sudo version 1.1) + * + * This software comes with no waranty whatsoever, use at your own risk. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + */ + +/* + * sudo version 1.1 allows users to execute commands as root + * Copyright (C) 1991 The Root Group, Inc. + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + **************************************************************** + * + * logging.c + * + * this file supports the general logging facilities + * if you want to change any error messages, this is probably + * the place to be... + * + * Jeff Nieusma Thu Mar 21 23:39:04 MST 1991 + */ + +#ifndef lint +static char rcsid[] = "$Id: logging.c,v 1.1 1996/10/14 05:14:50 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#include <pwd.h> +#include <signal.h> +#include <sys/types.h> +#include <sys/time.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/wait.h> +#include <sys/errno.h> +#include <netinet/in.h> + +#include "sudo.h" +#include <options.h> + +/* + * Prototypes for local functions + */ +static void send_mail __P((void)); +static RETSIGTYPE reapchild __P((int)); +static int appropriate __P((int)); +#ifdef BROKEN_SYSLOG +static void syslog_wrapper __P((int, char *, char *, char *)); +#endif /* BROKEN_SYSLOG */ + +/* + * Globals + */ +static char *logline; +extern int errorlineno; + +/* + * length of syslog-like header info used for mail and file logs + * is len("Mon MM HH:MM:SS : username : ") + */ +#define LOG_HEADER_LEN 29 + +#ifdef BROKEN_SYSLOG +#define MAXSYSLOGTRIES 16 /* num of retries for broken syslogs */ +#define SYSLOG(a,b,c,d) syslog_wrapper(a,b,c,d) + +/**************************************************************** + * + * syslog_wrapper() + * + * This function logs via syslog w/ a priority and 3 strings args. + * It really shouldn't be necesary but some syslog()'s don't + * guarantee that the syslog() operation will succeed! + */ + +static void syslog_wrapper(pri, fmt, arg1, arg2) + int pri; + char *fmt; + char *arg1; + char *arg2; +{ + int i; + + for (i = 0; i < MAXSYSLOGTRIES; i++) + if (syslog(pri, fmt, arg1, arg2) == 0) + break; +} +#else +#define SYSLOG(a,b,c,d) syslog(a,b,c,d) +#endif /* BROKEN_SYSLOG */ + + + +/********************************************************************** + * + * log_error() + * + * This function attempts to deliver mail to ALERTMAIL and either + * syslogs the error or writes it to the log file + */ + +void log_error(code) + int code; +{ + char *p; + int count; + time_t now; +#if (LOGGING & SLOG_FILE) + mode_t oldmask; + FILE *fp; +#endif /* LOGGING & SLOG_FILE */ +#if (LOGGING & SLOG_SYSLOG) + int pri = Syslog_priority_NO; /* syslog priority, assume the worst */ + char *tmp, save; +#endif /* LOGGING & SLOG_SYSLOG */ + + /* + * Allocate enough memory for logline so we won't overflow it + */ + count = LOG_HEADER_LEN + 136 + 2 * MAXPATHLEN + strlen(tty) + strlen(cwd) + + strlen(runas_user); + if (cmnd_args) + count += strlen(cmnd_args); + + logline = (char *) malloc(count); + if (logline == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* + * we will skip this stuff when using syslog(3) but it is + * necesary for mail and file logs. + */ + now = time((time_t) 0); + p = ctime(&now) + 4; + (void) sprintf(logline, "%15.15s : %8.8s : ", p, user_name); + + /* + * we need a pointer to the end of logline for cheap appends. + */ + p = logline + LOG_HEADER_LEN; + + switch (code) { + + case ALL_SYSTEMS_GO: + (void) sprintf(p, "TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + tty, cwd, runas_user); +#if (LOGGING & SLOG_SYSLOG) + pri = Syslog_priority_OK; +#endif /* LOGGING & SLOG_SYSLOG */ + break; + + case VALIDATE_NO_USER: + (void) sprintf(p, + "user NOT in sudoers ; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + tty, cwd, runas_user); + break; + + case VALIDATE_NOT_OK: + (void) sprintf(p, + "command not allowed ; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + tty, cwd, runas_user); + break; + + case VALIDATE_ERROR: + (void) sprintf(p, "error in %s, line %d ; TTY=%s ; PWD=%s ; USER=%s. ", + _PATH_SUDO_SUDOERS, errorlineno, tty, cwd, runas_user); + break; + + case GLOBAL_NO_PW_ENT: + (void) sprintf(p, + "There is no passwd entry for uid %ld (TTY=%s). ", + (long) user_uid, tty); + break; + + case PASSWORD_NOT_CORRECT: + (void) sprintf(p, + "password incorrect ; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + tty, cwd, runas_user); + break; + + case PASSWORDS_NOT_CORRECT: + (void) sprintf(p, + "%d incorrect passwords ; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + TRIES_FOR_PASSWORD, tty, cwd, runas_user); + break; + + case GLOBAL_NO_HOSTNAME: + strcat(p, "This machine does not have a hostname "); + break; + + case NO_SUDOERS_FILE: + switch (errno) { + case ENOENT: + (void) sprintf(p, "There is no %s file. ", + _PATH_SUDO_SUDOERS); + break; + case EACCES: + (void) sprintf(p, "Can't read %s. ", _PATH_SUDO_SUDOERS); + break; + default: + (void) sprintf(p, "There is a problem opening %s ", + _PATH_SUDO_SUDOERS); + break; + } + break; + + case GLOBAL_HOST_UNREGISTERED: + (void) sprintf(p, "gethostbyname() cannot find host %s ", host); + break; + + case SUDOERS_NOT_FILE: + (void) sprintf(p, "%s is not a regular file ", _PATH_SUDO_SUDOERS); + break; + + case SUDOERS_WRONG_OWNER: + (void) sprintf(p, "%s is not owned by uid %d and gid %d ", + _PATH_SUDO_SUDOERS, SUDOERS_UID, SUDOERS_GID); + break; + + case SUDOERS_WRONG_MODE: + (void) sprintf(p, "%s is not mode %o ", _PATH_SUDO_SUDOERS, + SUDOERS_MODE); + break; + + case SPOOF_ATTEMPT: + (void) sprintf(p, + "probable spoofing attempt; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + tty, cwd, runas_user); + break; + + case BAD_STAMPDIR: + (void) sprintf(p, + "%s owned by non-root or not mode 0700; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + _PATH_SUDO_TIMEDIR, tty, cwd, runas_user); + break; + + case BAD_STAMPFILE: + (void) sprintf(p, + "preposterous stampfile date; TTY=%s ; PWD=%s ; USER=%s ; COMMAND=", + tty, cwd, runas_user); + break; + + default: + strcat(p, "found a wierd error : "); + break; + } + + + /* + * If this is a parse error or if the error is from load_globals() + * don't put argv in the message. + */ + if (code != VALIDATE_ERROR && !(code & GLOBAL_PROBLEM)) { + + /* stuff the command into the logline */ + p = logline + strlen(logline); + strcpy(p, cmnd); + + /* add a trailing space */ + p += strlen(cmnd); + *p++ = ' '; + *p = '\0'; + + /* cat on command args if they exist */ + if (cmnd_args) { + (void) strcpy(p, cmnd_args); + p += strlen(cmnd_args); + *p++ = ' '; + *p = '\0'; + } + } + +#if (LOGGING & SLOG_SYSLOG) +#ifdef Syslog_facility + openlog(Syslog_ident, Syslog_options, Syslog_facility); +#else + openlog(Syslog_ident, Syslog_options); +#endif /* Syslog_facility */ + + /* + * Log the full line, breaking into multiple syslog(3) calls if necesary + */ + p = &logline[LOG_HEADER_LEN]; /* skip past the date and user */ + for (count = 0; count < strlen(logline) / MAXSYSLOGLEN + 1; count++) { + if (strlen(p) > MAXSYSLOGLEN) { + /* + * Break up the line into what will fit on one syslog(3) line + * Try to break on a word boundary if possible. + */ + for (tmp = p + MAXSYSLOGLEN; tmp > p && *tmp != ' '; tmp--) + ; + if (tmp <= p) + tmp = p + MAXSYSLOGLEN; + + /* NULL terminate line, but save the char to restore later */ + save = *tmp; + *tmp = '\0'; + + if (count == 0) + SYSLOG(pri, "%8.8s : %s", user_name, p); + else + SYSLOG(pri, "%8.8s : (command continued) %s", user_name, p); + + *tmp = save; /* restore saved character */ + + /* eliminate leading whitespace */ + for (p=tmp; *p != ' '; p++) + ; + } else { + if (count == 0) + SYSLOG(pri, "%8.8s : %s", user_name, p); + else + SYSLOG(pri, "%8.8s : (command continued) %s", user_name, p); + } + } + closelog(); +#endif /* LOGGING & SLOG_SYSLOG */ +#if (LOGGING & SLOG_FILE) + + /* become root */ + set_perms(PERM_ROOT, 0); + + oldmask = umask(077); + fp = fopen(_PATH_SUDO_LOGFILE, "a"); + (void) umask(oldmask); + if (fp == NULL) { + (void) sprintf(logline, "Can\'t open log file: %s", _PATH_SUDO_LOGFILE); + send_mail(); + } else { + char *beg, *oldend, *end; + int maxlen = MAXLOGFILELEN; + + /* + * Print out logline with word wrap + */ + beg = end = logline; + while (beg) { + oldend = end; + end = strchr(oldend, ' '); + + if (end) { + *end = '\0'; + if (strlen(beg) > maxlen) { + /* too far, need to back up & print the line */ + + if (beg == (char *)logline) + maxlen -= 4; /* don't indent first line */ + + *end = ' '; + if (oldend != beg) { + /* rewind & print */ + end = oldend-1; + while (*end == ' ') + --end; + *(++end) = '\0'; + (void) fprintf(fp, "%s\n ", beg); + *end = ' '; + } else { + (void) fprintf(fp, "%s\n ", beg); + } + + /* reset beg to point to the start of the new substring */ + beg = end; + while (*beg == ' ') + ++beg; + } else { + /* we still have room */ + *end = ' '; + } + + /* remove leading whitespace */ + while (*end == ' ') + ++end; + } else { + /* final line */ + (void) fprintf(fp, "%s\n", beg); + beg = NULL; /* exit condition */ + } + } + + (void) fclose(fp); + } + + /* relinquish root */ + set_perms(PERM_USER, 0); +#endif /* LOGGING & SLOG_FILE */ + + /* send mail if appropriate */ + if (appropriate(code)) + send_mail(); +} + + + +#ifdef MAILER +/********************************************************************** + * + * send_mail() + * + * This function attempts to mail to ALERTMAIL about the sudo error + * + */ + +static char *mail_argv[] = { "sendmail", "-t", (char *) NULL }; + +static void send_mail() +{ + char *mailer = MAILER; + char *subject = MAILSUBJECT; + int fd[2]; +#ifdef POSIX_SIGNALS + struct sigaction action; + + (void) memset((VOID *)&action, 0, sizeof(action)); +#endif /* POSIX_SIGNALS */ + + /* catch children as they die */ +#ifdef POSIX_SIGNALS + action.sa_handler = reapchild; + (void) sigaction(SIGCHLD, &action, NULL); +#else + (void) signal(SIGCHLD, reapchild); +#endif /* POSIX_SIGNALS */ + + if (fork()) + return; + + /* + * we don't want any security problems ... + */ + set_perms(PERM_FULL_USER, 0); + +#ifdef POSIX_SIGNALS + action.sa_handler = SIG_IGN; + (void) sigaction(SIGHUP, &action, NULL); + (void) sigaction(SIGINT, &action, NULL); + (void) sigaction(SIGQUIT, &action, NULL); +#else + (void) signal(SIGHUP, SIG_IGN); + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); +#endif /* POSIX_SIGNALS */ + + if (pipe(fd)) { + perror("send_mail: pipe"); + exit(1); + } + (void) dup2(fd[0], 0); + (void) dup2(fd[1], 1); + (void) close(fd[0]); + (void) close(fd[1]); + + if (!fork()) { /* child */ + (void) close(1); + EXEC(mailer, mail_argv); + + /* this should not happen */ + perror(mailer); + exit(1); + } else { /* parent */ + (void) close(0); + + /* feed the data to sendmail */ + /* XXX - do we need to fdopen this fd #1 to a new stream??? */ + (void) fprintf(stdout, "To: %s\nSubject: %s\n\n%s : %s\n\n", + ALERTMAIL, subject, host, logline); + fclose(stdout); + + exit(0); + } +} +#else +static void send_mail() +{ + /* no mailer defined */ + return; +} +#endif /* MAILER */ + + + +/**************************************************************** + * + * reapchild() + * + * This function gets rid of all the ugly zombies + */ + +static RETSIGTYPE reapchild(sig) + int sig; +{ + (void) wait(NULL); +#ifndef POSIX_SIGNALS + (void) signal(SIGCHLD, reapchild); +#endif /* POSIX_SIGNALS */ +} + + + +/********************************************************************** + * + * inform_user () + * + * This function lets the user know what is happening + * when an error occurs + */ + +void inform_user(code) + int code; +{ + switch (code) { + case VALIDATE_NO_USER: + (void) fprintf(stderr, + "%s is not in the sudoers file. This incident will be reported.\n\n", + user_name); + break; + + case VALIDATE_NOT_OK: + (void) fprintf(stderr, + "Sorry, user %s is not allowed to execute \"%s", + user_name, cmnd); + + /* print command args if they exist */ + if (cmnd_args) { + fputc(' ', stderr); + fputs(cmnd_args, stderr); + } + + (void) fprintf(stderr, "\" as %s on %s.\n\n", runas_user, host); + break; + + case VALIDATE_ERROR: + (void) fprintf(stderr, + "Sorry, there is a fatal error in the sudoers file.\n\n"); + break; + + case GLOBAL_NO_PW_ENT: + (void) fprintf(stderr, + "Intruder Alert! You don't exist in the passwd file\n\n"); + break; + + case GLOBAL_NO_SPW_ENT: + (void) fprintf(stderr, + "Intruder Alert! You don't exist in the shadow passwd file\n\n"); + break; + + case GLOBAL_NO_HOSTNAME: + (void) fprintf(stderr, + "This machine does not have a hostname\n\n"); + break; + + case GLOBAL_HOST_UNREGISTERED: + (void) fprintf(stderr, + "This machine is not available via gethostbyname()\n\n"); + break; + + case PASSWORD_NOT_CORRECT: + (void) fprintf(stderr, "Password not entered correctly\n\n"); + break; + + case PASSWORDS_NOT_CORRECT: + (void) fprintf(stderr, "Password not entered correctly after %d tries\n\n", + TRIES_FOR_PASSWORD); + break; + + case NO_SUDOERS_FILE: + switch (errno) { + case ENOENT: + (void) fprintf(stderr, "There is no %s file.\n", + _PATH_SUDO_SUDOERS); + break; + default: + (void) fprintf(stderr, "Can't read %s: ", + _PATH_SUDO_SUDOERS); + perror(""); + break; + } + break; + + case SUDOERS_NOT_FILE: + (void) fprintf(stderr, + "%s is not a regular file!\n", _PATH_SUDO_SUDOERS); + break; + + case SUDOERS_WRONG_OWNER: + (void) fprintf(stderr, "%s is not owned by uid %d and gid %d!\n", + _PATH_SUDO_SUDOERS, SUDOERS_UID, SUDOERS_GID); + break; + + case SUDOERS_WRONG_MODE: + (void) fprintf(stderr, "%s must be mode %o!\n", _PATH_SUDO_SUDOERS, + SUDOERS_MODE); + break; + + case SPOOF_ATTEMPT: + (void) fprintf(stderr, + "%s is not the same command that was validated, disallowing.\n", + cmnd); + break; + + case BAD_STAMPDIR: + (void) fprintf(stderr, + "Timestamp directory has wrong permissions, ignoring.\n"); + break; + + case BAD_STAMPFILE: + (void) fprintf(stderr, + "Your timestamp file has a preposterous date, ignoring.\n"); + break; + + default: + (void) fprintf(stderr, + "Something wierd happened.\n\n"); + break; + } +} + + + +/**************************************************************** + * + * appropriate() + * + * This function determines whether to send mail or not... + */ + +static int appropriate(code) + int code; +{ + + switch (code) { + + /* + * these will NOT send mail + */ + case VALIDATE_OK: + case VALIDATE_OK_NOPASS: + case PASSWORD_NOT_CORRECT: + case PASSWORDS_NOT_CORRECT: +/* case ALL_SYSTEMS_GO: this is the same as OK */ + return (0); + break; + + case VALIDATE_NO_USER: +#ifdef SEND_MAIL_WHEN_NO_USER + return (1); +#else + return (0); +#endif + break; + + case VALIDATE_NOT_OK: +#ifdef SEND_MAIL_WHEN_NOT_OK + return (1); +#else + return (0); +#endif + break; + + /* + * these WILL send mail + */ + case VALIDATE_ERROR: + case NO_SUDOERS_FILE: + case SPOOF_ATTEMPT: + case BAD_STAMPDIR: + case BAD_STAMPFILE: + default: + return (1); + break; + + } +} diff --git a/gnu/usr.bin/sudo/sudo/options.h b/gnu/usr.bin/sudo/sudo/options.h new file mode 100644 index 00000000000..47b7ceb3ea5 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/options.h @@ -0,0 +1,110 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: options.h,v 1.1 1996/10/14 05:14:50 millert Exp $ + */ + +#ifndef _SUDO_OPTIONS_H +#define _SUDO_OPTIONS_H + +/* + * DANGER DANGER DANGER! + * Before you change anything here read through the OPTIONS file + * for a description of what this stuff does. + */ + +/* User-configurable Sudo runtime options */ + +/*#define FQDN /* expect fully qualified hosts in sudoers */ +#define LOGGING SLOG_SYSLOG /* log via SLOG_SYSLOG, SLOG_FILE, SLOG_BOTH */ +#define LOGFAC LOG_LOCAL2 /* syslog facility for sudo to use */ +#define MAXLOGFILELEN 80 /* max chars per log line (for line wrapping) */ +/*#define NO_ROOT_SUDO /* root is not allowed to use sudo */ +#define ALERTMAIL "root" /* user that gets sudo mail */ +#define SEND_MAIL_WHEN_NO_USER /* send mail when user not in sudoers file */ +/*#define SEND_MAIL_WHEN_NOT_OK /* send mail if no permissions to run command */ +/*#define EXEMPTGROUP "sudo" /* no passwd needed for users in this group */ +#define ENV_EDITOR /* visudo honors EDITOR and VISUAL envars */ +#define SHORT_MESSAGE /* short sudo message, no copyright printed */ +/*#define NO_MESSAGE /* no sudo "lecture" message */ +#define TIMEOUT 5 /* minutes before sudo asks for passwd again */ +#define PASSWORD_TIMEOUT 0 /* passwd prompt timeout (in minutes) */ +#define TRIES_FOR_PASSWORD 3 /* number of tries to enter passwd correctly */ +#define USE_INSULTS /* insult the user for incorrect passwords */ +#define CLASSIC_INSULTS /* sudo "classic" insults--need USE_INSULTS */ +/*#define HAL_INSULTS /* 2001-like insults--must define USE_INSULTS */ +/*#define GOONS_INSULTS /* Goon Show insults--must define USE_INSULTS */ +#define CSOPS_INSULTS /* CSOps insults--must define USE_INSULTS */ +#define EDITOR _PATH_VI /* default editor to use */ +#define MAILER _PATH_SENDMAIL /* what mailer to use */ +#define UMASK 0022 /* umask that the root-run prog should use */ +#define INCORRECT_PASSWORD "Sorry, try again." /* message for bad passwd */ +#define MAILSUBJECT "*** SECURITY information ***" /* subject of mail sent */ +#define PASSPROMPT "Password:" /* default password prompt */ +/*#define IGNORE_DOT_PATH /* ignore '.' in $PATH if it exists */ +/*#define SECURE_PATH "/bin:/usr/ucb:/usr/bin:/usr/etc:/etc" /* secure path */ +/*#define USE_EXECV /* use execv() instead of execvp() */ +/*#define SHELL_IF_NO_ARGS /* if sudo is given no arguments run a shell */ +/*#define SHELL_SETS_HOME /* -s sets $HOME to runas user's homedir */ +/*#define USE_TTY_TICKETS /* have a different ticket file for each tty */ +/*#define OTP_ONLY /* validate user via OTP (skey/opie) only */ +/*#define LONG_OTP_PROMPT /* use a two line OTP (skey/opie) prompt */ +#define FAST_MATCH /* command check fails if basenames not same */ +#ifndef SUDOERS_MODE +#define SUDOERS_MODE 0440 /* file mode for sudoers (octal) */ +#endif /* SUDOERS_MODE */ +#ifndef SUDOERS_UID +#define SUDOERS_UID 0 /* user id that owns sudoers (*not* a name) */ +#endif /* SUDOERS_UID */ +#ifndef SUDOERS_GID +#define SUDOERS_GID 0 /* group id that owns sudoers (*not* a name) */ +#endif /* SUDOERS_GID */ + +/********** You probably don't want to modify anything below here ***********/ + +#ifdef USE_EXECV +# define EXEC execv +#else +# define EXEC execvp +#endif /* USE_EXECV */ + +/* + * syslog(3) parameters + */ + +#if (LOGGING & SLOG_SYSLOG) +# include <syslog.h> +# ifndef Syslog_ident +# define Syslog_ident "sudo" +# endif +# ifndef Syslog_options +# define Syslog_options 0 +# endif +# if !defined(Syslog_facility) && defined(LOG_NFACILITIES) +# define Syslog_facility LOGFAC +# endif +# ifndef Syslog_priority_OK +# define Syslog_priority_OK LOG_NOTICE +# endif +# ifndef Syslog_priority_NO +# define Syslog_priority_NO LOG_ALERT +# endif +#endif /* LOGGING & SLOG_SYSLOG */ + +#endif /* _SUDO_OPTIONS_H */ diff --git a/gnu/usr.bin/sudo/sudo/parse.c b/gnu/usr.bin/sudo/sudo/parse.c new file mode 100644 index 00000000000..1e09e3eacf9 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/parse.c @@ -0,0 +1,446 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * parse.c -- sudo parser frontend and comparison routines. + * + * Chris Jepeway <jepeway@cs.utk.edu> + */ + +#ifndef lint +static char rcsid[] = "$Id: parse.c,v 1.1 1996/10/14 05:14:51 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +# include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +# include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +# include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +# include <strings.h> +#endif /* HAVE_STRINGS_H */ +#if defined(HAVE_FNMATCH) && defined(HAVE_FNMATCH_H) +# include <fnmatch.h> +#else +# ifndef HAVE_FNMATCH +# include "emul/fnmatch.h" +# endif /* HAVE_FNMATCH */ +#endif /* HAVE_FNMATCH_H */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +# include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#ifdef HAVE_NETGROUP_H +# include <netgroup.h> +#endif /* HAVE_NETGROUP_H */ +#include <ctype.h> +#include <pwd.h> +#include <grp.h> +#include <sys/param.h> +#include <sys/types.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <sys/stat.h> +#if HAVE_DIRENT_H +# include <dirent.h> +# define NAMLEN(dirent) strlen((dirent)->d_name) +#else +# define dirent direct +# define NAMLEN(dirent) (dirent)->d_namlen +# if HAVE_SYS_NDIR_H +# include <sys/ndir.h> +# endif +# if HAVE_SYS_DIR_H +# include <sys/dir.h> +# endif +# if HAVE_NDIR_H +# include <ndir.h> +# endif +#endif + +#include "sudo.h" +#include <options.h> + +/* + * Globals + */ +int parse_error = FALSE; +extern FILE *yyin, *yyout; +extern int printmatches; + +/* + * Prototypes + */ +static int has_meta __P((char *)); + void init_parser __P((void)); + +/* + * This routine is called from the sudo.c module and tries to validate + * the user, host and command triplet. + */ +int validate(check_cmnd) + int check_cmnd; +{ + FILE *sudoers_fp; + int return_code; + + /* become sudoers file owner */ + set_perms(PERM_SUDOERS, 0); + + if ((sudoers_fp = fopen(_PATH_SUDO_SUDOERS, "r")) == NULL) { + perror(_PATH_SUDO_SUDOERS); + log_error(NO_SUDOERS_FILE); + exit(1); + } + yyin = sudoers_fp; + yyout = stdout; + + /* + * Allocate space for data structures in the parser. + */ + init_parser(); + + /* + * Need to be root while stat'ing things in the parser. + */ + set_perms(PERM_ROOT, 0); + return_code = yyparse(); + + /* + * Don't need to keep this open... + */ + (void) fclose(sudoers_fp); + + /* relinquish extra privs */ + set_perms(PERM_USER, 0); + + if (return_code || parse_error) + return(VALIDATE_ERROR); + + /* + * Nothing on the top of the stack => user doesn't appear in sudoers. + * Allow anyone to try the psuedo commands "list" and "validate". + */ + if (top == 0) { + if (check_cmnd == TRUE) + return(VALIDATE_NO_USER); + else + return(VALIDATE_NOT_OK); + } + + /* + * Only check the actual command if the check_cmnd + * flag is set. It is not set for the "validate" + * and "list" pseudo-commands. Always check the + * host and user. + */ + if (check_cmnd == FALSE) + while (top) { + if (host_matches == TRUE) + /* user may always do validate or list on allowed hosts */ + if (no_passwd == TRUE) + return(VALIDATE_OK_NOPASS); + else + return(VALIDATE_OK); + top--; + } + else + while (top) { + if (host_matches == TRUE) { + if (cmnd_matches == TRUE) { + if (runas_matches == TRUE) { + /* + * User was granted access to cmnd on host. + * If no passwd required return as such. + */ + if (no_passwd == TRUE) + return(VALIDATE_OK_NOPASS); + else + return(VALIDATE_OK); + } + } else if (cmnd_matches == FALSE) { + /* User was explicitly denied acces to cmnd on host. */ + return(VALIDATE_NOT_OK); + } + } + top--; + } + + /* + * we popped everything off the stack => + * user was mentioned, but not explicitly + * granted nor denied access => say no + */ + return(VALIDATE_NOT_OK); +} + + + +/* + * If path doesn't end in /, return TRUE iff cmnd & path name the same inode; + * otherwise, return TRUE if cmnd names one of the inodes in path. + */ +int command_matches(cmnd, user_args, path, sudoers_args) + char *cmnd; + char *user_args; + char *path; + char *sudoers_args; +{ + int plen; + struct stat pst; + DIR *dirp; + struct dirent *dent; + char buf[MAXPATHLEN+1]; + static char *c; + + /* don't bother with pseudo commands like "validate" */ + if (*cmnd != '/') + return(FALSE); + + /* only need to stat cmnd once since it never changes */ + if (cmnd_st.st_dev == 0) { + if (stat(cmnd, &cmnd_st) < 0) + return(FALSE); + if ((c = strrchr(cmnd, '/')) == NULL) + c = cmnd; + else + c++; + } + + /* + * If the pathname has meta characters in it use fnmatch(3) + * to do the matching + */ + if (has_meta(path)) { + /* + * Return true if fnmatch(3) succeeds and there are no args + * (in sudoers or command) or if the args match; + * else return false. + */ + if (fnmatch(path, cmnd, FNM_PATHNAME)) + return(FALSE); + if (!sudoers_args) + return(TRUE); + else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) + return(TRUE); + else if (sudoers_args) + return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)); + else + return(FALSE); + } else { + plen = strlen(path); + if (path[plen - 1] != '/') { +#ifdef FAST_MATCH + char *p; + + /* Only proceed if the basenames of cmnd and path are the same */ + if ((p = strrchr(path, '/')) == NULL) + p = path; + else + p++; + if (strcmp(c, p)) + return(FALSE); +#endif /* FAST_MATCH */ + + if (stat(path, &pst) < 0) + return(FALSE); + + /* + * Return true if inode/device matches and there are no args + * (in sudoers or command) or if the args match; + * else return false. + */ + if (cmnd_st.st_dev != pst.st_dev || cmnd_st.st_ino != pst.st_ino) + return(FALSE); + if (!sudoers_args) + return(TRUE); + else if (!user_args && sudoers_args && !strcmp("\"\"", sudoers_args)) + return(TRUE); + else if (sudoers_args) + return((fnmatch(sudoers_args, user_args ? user_args : "", 0) == 0)); + else + return(FALSE); + } + + /* + * Grot through path's directory entries, looking for cmnd. + */ + dirp = opendir(path); + if (dirp == NULL) + return(FALSE); + + while ((dent = readdir(dirp)) != NULL) { + strcpy(buf, path); + strcat(buf, dent->d_name); +#ifdef FAST_MATCH + /* only stat if basenames are not the same */ + if (strcmp(c, dent->d_name)) + continue; +#endif /* FAST_MATCH */ + if (stat(buf, &pst) < 0) + continue; + if (cmnd_st.st_dev == pst.st_dev && cmnd_st.st_ino == pst.st_ino) + break; + } + + closedir(dirp); + return(dent != NULL); + } +} + + + +/* + * Returns TRUE if "n" is one of our ip addresses or if + * "n" is a network that we are on, else returns FALSE. + */ +int addr_matches(n) + char *n; +{ + int i; + char *m; + struct in_addr addr, mask; + + /* If there's an explicate netmask, use it. */ + if ((m = strchr(n, '/'))) { + *m++ = '\0'; + mask.s_addr = inet_addr(m); + addr.s_addr = inet_addr(n); + *(m - 1) = '/'; + + for (i = 0; i < num_interfaces; i++) + if ((interfaces[i].addr.s_addr & mask.s_addr) == addr.s_addr) + return(TRUE); + } else { + addr.s_addr = inet_addr(n); + + for (i = 0; i < num_interfaces; i++) + if (interfaces[i].addr.s_addr == addr.s_addr || + (interfaces[i].addr.s_addr & interfaces[i].netmask.s_addr) + == addr.s_addr) + return(TRUE); + } + + return(FALSE); +} + + + +/* + * Returns TRUE if the given user belongs to the named group, + * else returns FALSE. + */ +int usergr_matches(group, user) + char *group; + char *user; +{ + struct group *grpent; + char **cur; + + /* make sure we have a valid usergroup, sudo style */ + if (*group++ != '%') + return(FALSE); + + if ((grpent = getgrnam(group)) == NULL) + return(FALSE); + + /* + * Check against user's real gid as well as group's user list + */ + if (grpent->gr_gid == user_gid) + return(TRUE); + + for (cur=grpent->gr_mem; *cur; cur++) { + if (strcmp(*cur, user) == 0) + return(TRUE); + } + + return(FALSE); +} + + + +/* + * Returns TRUE if "host" and "user" belong to the netgroup "netgr", + * else return FALSE. Either of "host" or "user" may be NULL + * in which case that argument is not checked... + */ +int netgr_matches(netgr, host, user) + char *netgr; + char *host; + char *user; +{ +#ifdef HAVE_GETDOMAINNAME + static char *domain = (char *) -1; +#else + static char *domain = NULL; +#endif /* HAVE_GETDOMAINNAME */ + + /* make sure we have a valid netgroup, sudo style */ + if (*netgr++ != '+') + return(FALSE); + +#ifdef HAVE_GETDOMAINNAME + /* get the domain name (if any) */ + if (domain == (char *) -1) { + if ((domain = (char *) malloc(MAXHOSTNAMELEN + 1)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + if (getdomainname(domain, MAXHOSTNAMELEN + 1) != 0 || *domain == '\0') { + (void) free(domain); + domain = NULL; + } + } +#endif /* HAVE_GETDOMAINNAME */ + +#ifdef HAVE_INNETGR + return(innetgr(netgr, host, user, domain)); +#else + return(FALSE); +#endif /* HAVE_INNETGR */ +} + + + +/* + * Returns TRUE if "s" has shell meta characters in it, + * else returns FALSE. + */ +static int has_meta(s) + char *s; +{ + register char *t; + + for (t = s; *t; t++) { + if (*t == '\\' || *t == '?' || *t == '*' || *t == '[' || *t == ']') + return(TRUE); + } + return(FALSE); +} diff --git a/gnu/usr.bin/sudo/sudo/parse.lex b/gnu/usr.bin/sudo/sudo/parse.lex new file mode 100644 index 00000000000..b2b541a4909 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/parse.lex @@ -0,0 +1,354 @@ +%{ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * parse.lex -- lexigraphical analyzer for sudo. + * + * Chris Jepeway <jepeway@cs.utk.edu> + */ + +#ifndef lint +static char rcsid[] = "$Id: parse.lex,v 1.1 1996/10/14 05:14:51 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#include <ctype.h> +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#include "sudo.h" +#include <options.h> +#include "y.tab.h" + +#undef yywrap /* guard against a yywrap macro */ + +extern YYSTYPE yylval; +extern int clearaliases; +int sudolineno = 1; +static int sawspace = 0; +static int arg_len = 0; +static int arg_size = 0; + +static void fill __P((char *, int)); +static void fill_cmnd __P((char *, int)); +static void fill_args __P((char *, int, int)); +extern void reset_aliases __P((void)); +extern void yyerror __P((char *)); + +/* realloc() to size + COMMANDARGINC to make room for command args */ +#define COMMANDARGINC 64 + +#ifdef TRACELEXER +#define LEXTRACE(msg) fputs(msg, stderr) +#else +#define LEXTRACE(msg) +#endif +%} + +OCTET [[:digit:]]{1,3} +DOTTEDQUAD {OCTET}(\.{OCTET}){3} +WORD [[:alnum:]_-]+ + +%e 4000 +%p 6000 +%k 3500 + +%s GOTCMND +%s GOTRUNAS + +%% +[ \t]+ { /* throw away space/tabs */ + sawspace = TRUE; /* but remember for fill_args */ + } + +\\\n { + sawspace = TRUE; /* remember for fill_args */ + ++sudolineno; + LEXTRACE("\n\t"); + } /* throw away EOL after \ */ + +<GOTCMND>\\[:\,=\\ \t] { + LEXTRACE("QUOTEDCHAR "); + fill_args(yytext + 1, 1, sawspace); + sawspace = FALSE; + } + +<GOTCMND>[:\,=\n] { + BEGIN INITIAL; + unput(*yytext); + return(COMMAND); + } /* end of command line args */ + +\n { + ++sudolineno; + LEXTRACE("\n"); + return(COMMENT); + } /* return newline */ + +<INITIAL>#.*\n { + ++sudolineno; + LEXTRACE("\n"); + return(COMMENT); + } /* return comments */ + +<GOTCMND>[^:\,= \t\n]+ { + LEXTRACE("ARG "); + fill_args(yytext, yyleng, sawspace); + sawspace = FALSE; + } /* a command line arg */ + +\, { + LEXTRACE(", "); + return(','); + } /* return ',' */ + +\! { + return('!'); /* return '!' */ + } + += { + LEXTRACE("= "); + return('='); + } /* return '=' */ + +: { + LEXTRACE(": "); + return(':'); + } /* return ':' */ + +\. { + return('.'); + } + +NOPASSWD[[:blank:]]*: { + /* cmnd does not require passwd for this user */ + LEXTRACE("NOPASSWD "); + return(NOPASSWD); + } + +\+{WORD} { + /* netgroup */ + fill(yytext, yyleng); + return(NETGROUP); + } + +\%{WORD} { + /* UN*X group */ + fill(yytext, yyleng); + return(USERGROUP); + } + +{DOTTEDQUAD}(\/{DOTTEDQUAD})? { + fill(yytext, yyleng); + LEXTRACE("NTWKADDR "); + return(NTWKADDR); + } + +[[:alpha:]][[:alnum:]_-]*(\.{WORD})+ { + fill(yytext, yyleng); + LEXTRACE("FQHOST "); + return(FQHOST); + } + +<INITIAL>\( { + BEGIN GOTRUNAS; + LEXTRACE("RUNAS "); + return (RUNAS); + } + +<GOTRUNAS>[[:upper:]][[:upper:][:digit:]_]* { + /* User_Alias that user can run command as or ALL */ + fill(yytext, yyleng); + if (strcmp(yytext, "ALL") == 0) { + LEXTRACE("ALL "); + return(ALL); + } else { + LEXTRACE("ALIAS "); + return(ALIAS); + } + } + +<GOTRUNAS>#?{WORD} { + /* username/uid that user can run command as */ + fill(yytext, yyleng); + LEXTRACE("NAME "); + return(NAME); + } + +<GOTRUNAS>\) BEGIN INITIAL; + + +\/[^\,:=\\ \t\n#]+ { + /* directories can't have args... */ + if (yytext[yyleng - 1] == '/') { + LEXTRACE("COMMAND "); + fill_cmnd(yytext, yyleng); + return(COMMAND); + } else { + BEGIN GOTCMND; + LEXTRACE("COMMAND "); + fill_cmnd(yytext, yyleng); + } + } /* a pathname */ + +[[:upper:]][[:upper:][:digit:]_]* { + fill(yytext, yyleng); + if (strcmp(yytext, "ALL") == 0) { + LEXTRACE("ALL "); + return(ALL); + } + LEXTRACE("ALIAS "); + return(ALIAS); + } + +[[:alnum:]][[:alnum:]_-]* { + int l; + + fill(yytext, yyleng); + if (strcmp(yytext, "Host_Alias") == 0) { + LEXTRACE("HOSTALIAS "); + return(HOSTALIAS); + } + if (strcmp(yytext, "Cmnd_Alias") == 0) { + LEXTRACE("CMNDALIAS "); + return(CMNDALIAS); + } + if (strcmp(yytext, "User_Alias") == 0) { + LEXTRACE("USERALIAS "); + return(USERALIAS); + } + l = yyleng - 1; + if (isalpha(yytext[l]) || isdigit(yytext[l])) { + /* NAME is what RFC1034 calls a label */ + LEXTRACE("NAME "); + return(NAME); + } + + return(ERROR); + } + +. { + return(ERROR); + } /* parse error */ + +%% +static void fill(s, len) + char *s; + int len; +{ + yylval.string = (char *) malloc(len + 1); + if (yylval.string == NULL) + yyerror("unable to allocate memory"); + + /* copy the string and NULL-terminate it */ + (void) strncpy(yylval.string, s, len); + yylval.string[len] = '\0'; +} + + +static void fill_cmnd(s, len) + char *s; + int len; +{ + arg_len = arg_size = 0; + + yylval.command.cmnd = (char *) malloc(len + 1); + if (yylval.command.cmnd == NULL) + yyerror("unable to allocate memory"); + + /* copy the string and NULL-terminate it */ + (void) strncpy(yylval.command.cmnd, s, len); + yylval.command.cmnd[len] = '\0'; + + yylval.command.args = NULL; +} + + +static void fill_args(s, len, addspace) + char *s; + int len; + int addspace; +{ + int new_len; + char *p; + + /* + * If first arg, malloc() some room, else if we don't + * have enough space realloc() some more. + */ + if (yylval.command.args == NULL) { + addspace = 0; + new_len = len; + + while (new_len >= (arg_size += COMMANDARGINC)) + ; + + yylval.command.args = (char *) malloc(arg_size); + if (yylval.command.args == NULL) + yyerror("unable to allocate memory"); + } else { + new_len = arg_len + len + addspace; + + if (new_len >= arg_size) { + /* Allocate more space than we need for subsequent args */ + while (new_len >= (arg_size += COMMANDARGINC)) + ; + + yylval.command.args = (char *) realloc(yylval.command.args, arg_size); + if (yylval.command.args == NULL) + yyerror("unable to allocate memory"); + } + } + + /* Efficiently append the arg (with a leading space if needed). */ + p = yylval.command.args + arg_len; + if (addspace) + *p++ = ' '; + (void) strcpy(p, s); + arg_len = new_len; +} + + +int yywrap() +{ +#ifdef YY_NEW_FILE + YY_NEW_FILE; +#endif /* YY_NEW_FILE */ + + /* don't reset the aliases if called by testsudoers */ + if (clearaliases) + reset_aliases(); + + return(TRUE); +} diff --git a/gnu/usr.bin/sudo/sudo/parse.yacc b/gnu/usr.bin/sudo/sudo/parse.yacc new file mode 100644 index 00000000000..4508a99a411 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/parse.yacc @@ -0,0 +1,954 @@ +%{ + +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * parse.yacc -- yacc parser and alias manipulation routines for sudo. + * + * Chris Jepeway <jepeway@cs.utk.edu> + */ + +#ifndef lint +static char rcsid[] = "$Id: parse.yacc,v 1.1 1996/10/14 05:14:52 millert Exp $"; +#endif /* lint */ + +#include "config.h" +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#include <pwd.h> +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#ifdef HAVE_LSEARCH +#include <search.h> +#endif /* HAVE_LSEARCH */ + +#include <options.h> +#include "sudo.h" + +#ifndef HAVE_LSEARCH +#include "emul/search.h" +#endif /* HAVE_LSEARCH */ + +#ifndef HAVE_STRCASECMP +#define strcasecmp(a,b) strcmp(a,b) +#endif /* !HAVE_STRCASECMP */ + +/* + * Globals + */ +extern int sudolineno, parse_error; +int errorlineno = -1; +int clearaliases = 1; +int printmatches = FALSE; + +/* + * Alias types + */ +#define HOST 1 +#define CMND 2 +#define USER 3 + +/* + * The matching stack, initial space allocated in init_parser(). + */ +struct matchstack *match; +int top = 0, stacksize = 0; + +#define push \ + { \ + if (top > stacksize) { \ + while ((stacksize += STACKINCREMENT) < top); \ + match = (struct matchstack *) realloc(match, sizeof(struct matchstack) * stacksize); \ + if (match == NULL) { \ + perror("malloc"); \ + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); \ + exit(1); \ + } \ + } \ + match[top].user = -1; \ + match[top].cmnd = -1; \ + match[top].host = -1; \ + match[top].runas = -1; \ + match[top].nopass = -1; \ + top++; \ + } + +#define pop \ + { \ + if (top == 0) \ + yyerror("matching stack underflow"); \ + else \ + top--; \ + } + +/* + * The stack for printmatches. A list of allowed commands for the user. + */ +static struct command_match *cm_list = NULL; +static size_t cm_list_len = 0, cm_list_size = 0; + +/* + * List of Cmnd_Aliases and expansions for `sudo -l' + */ +static int in_alias = FALSE; +static size_t ca_list_len = 0, ca_list_size = 0; +static struct command_alias *ca_list = NULL; + +/* + * Protoypes + */ +extern int command_matches __P((char *, char *, char *, char *)); +extern int addr_matches __P((char *)); +extern int netgr_matches __P((char *, char *, char *)); +extern int usergr_matches __P((char *, char *)); +static int find_alias __P((char *, int)); +static int add_alias __P((char *, int)); +static int more_aliases __P((size_t)); +static void append __P((char *, char **, size_t *, size_t *, int)); +static void expand_ca_list __P((void)); +static void expand_match_list __P((void)); + void init_parser __P((void)); + void yyerror __P((char *)); + +void yyerror(s) + char *s; +{ + /* save the line the first error occured on */ + if (errorlineno == -1) + errorlineno = sudolineno; +#ifndef TRACELEXER + (void) fprintf(stderr, ">>> sudoers file: %s, line %d <<<\n", s, sudolineno); +#else + (void) fprintf(stderr, "<*> "); +#endif + parse_error = TRUE; +} +%} + +%union { + char *string; + int BOOLEAN; + struct sudo_command command; + int tok; +} + + +%start file /* special start symbol */ +%token <string> ALIAS /* an UPPERCASE alias name */ +%token <string> NTWKADDR /* w.x.y.z */ +%token <string> FQHOST /* foo.bar.com */ +%token <string> NETGROUP /* a netgroup (+NAME) */ +%token <string> USERGROUP /* a usergroup (%NAME) */ +%token <string> NAME /* a mixed-case name */ +%token <tok> RUNAS /* a mixed-case runas name */ +%token <tok> NOPASSWD /* no passwd req for command*/ +%token <command> COMMAND /* an absolute pathname */ +%token <tok> COMMENT /* comment and/or carriage return */ +%token <tok> ALL /* ALL keyword */ +%token <tok> HOSTALIAS /* Host_Alias keyword */ +%token <tok> CMNDALIAS /* Cmnd_Alias keyword */ +%token <tok> USERALIAS /* User_Alias keyword */ +%token <tok> ':' '=' ',' '!' '.' /* union member tokens */ +%token <tok> ERROR + +%type <BOOLEAN> cmnd +%type <BOOLEAN> opcmnd +%type <BOOLEAN> runasspec +%type <BOOLEAN> runaslist +%type <BOOLEAN> runasuser +%type <BOOLEAN> nopasswd + +%% + +file : entry + | file entry + ; + +entry : COMMENT + { ; } + | error COMMENT + { yyerrok; } + | { push; } user privileges { + while (top && user_matches != TRUE) { + pop; + } + } + | USERALIAS useraliases + { ; } + | HOSTALIAS hostaliases + { ; } + | CMNDALIAS cmndaliases + { ; } + ; + + +privileges : privilege + | privileges ':' privilege + ; + +privilege : hostspec '=' cmndspeclist { + if (user_matches == TRUE) { + push; + user_matches = TRUE; + } else { + no_passwd = -1; + runas_matches = -1; + } + } + ; + +hostspec : ALL { + host_matches = TRUE; + } + | NTWKADDR { + if (addr_matches($1)) + host_matches = TRUE; + (void) free($1); + } + | NETGROUP { + if (netgr_matches($1, host, NULL)) + host_matches = TRUE; + (void) free($1); + } + | NAME { + if (strcasecmp(shost, $1) == 0) + host_matches = TRUE; + (void) free($1); + } + | FQHOST { + if (strcasecmp(host, $1) == 0) + host_matches = TRUE; + (void) free($1); + } + | ALIAS { + /* could be an all-caps hostname */ + if (find_alias($1, HOST) || !strcasecmp(shost, $1)) + host_matches = TRUE; + (void) free($1); + } + ; + +cmndspeclist : cmndspec + | cmndspeclist ',' cmndspec + ; + +cmndspec : runasspec nopasswd opcmnd { + if ($1 > 0 && $3 == TRUE) { + runas_matches = TRUE; + if ($2 == TRUE) + no_passwd = TRUE; + } else if (printmatches == TRUE) { + cm_list[cm_list_len].runas_len = 0; + cm_list[cm_list_len].cmnd_len = 0; + cm_list[cm_list_len].nopasswd = FALSE; + } + } + ; + +opcmnd : cmnd { ; } + | '!' { + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) { + append("!", &cm_list[cm_list_len].cmnd, + &cm_list[cm_list_len].cmnd_len, + &cm_list[cm_list_len].cmnd_size, 0); + push; + user_matches = TRUE; + host_matches = TRUE; + } else { + push; + } + } opcmnd { + int cmnd_matched = cmnd_matches; + pop; + if (cmnd_matched == TRUE) + cmnd_matches = FALSE; + else if (cmnd_matched == FALSE) + cmnd_matches = TRUE; + $$ = cmnd_matches; + } + ; + +runasspec : /* empty */ { + $$ = (strcmp("root", runas_user) == 0); + } + | RUNAS runaslist { + $$ = $2; + } + ; + +runaslist : runasuser { + $$ = $1; + } + | runaslist ',' runasuser { + $$ = $1 + $3; + } + ; + + +runasuser : NAME { + $$ = (strcmp($1, runas_user) == 0); + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) + append($1, &cm_list[cm_list_len].runas, + &cm_list[cm_list_len].runas_len, + &cm_list[cm_list_len].runas_size, ':'); + (void) free($1); + } + | USERGROUP { + $$ = usergr_matches($1, runas_user); + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) { + append("%", &cm_list[cm_list_len].runas, + &cm_list[cm_list_len].runas_len, + &cm_list[cm_list_len].runas_size, ':'); + append($1, &cm_list[cm_list_len].runas, + &cm_list[cm_list_len].runas_len, + &cm_list[cm_list_len].runas_size, 0); + } + (void) free($1); + } + | NETGROUP { + $$ = netgr_matches($1, NULL, runas_user); + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) { + append("+", &cm_list[cm_list_len].runas, + &cm_list[cm_list_len].runas_len, + &cm_list[cm_list_len].runas_size, ':'); + append($1, &cm_list[cm_list_len].runas, + &cm_list[cm_list_len].runas_len, + &cm_list[cm_list_len].runas_size, 0); + } + (void) free($1); + } + | ALIAS { + /* could be an all-caps username */ + if (find_alias($1, USER) || !strcmp($1, runas_user)) + $$ = TRUE; + else + $$ = FALSE; + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) + append($1, &cm_list[cm_list_len].runas, + &cm_list[cm_list_len].runas_len, + &cm_list[cm_list_len].runas_size, ':'); + (void) free($1); + } + | ALL { + $$ = TRUE; + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) + append("ALL", &cm_list[cm_list_len].runas, + &cm_list[cm_list_len].runas_len, + &cm_list[cm_list_len].runas_size, ':'); + } + ; + +nopasswd : /* empty */ { + $$ = FALSE; + } + | NOPASSWD { + $$ = TRUE; + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) + cm_list[cm_list_len].nopasswd = TRUE; + } + ; + +cmnd : ALL { + if (printmatches == TRUE && in_alias == TRUE) { + append("ALL", &ca_list[ca_list_len-1].entries, + &ca_list[ca_list_len-1].entries_len, + &ca_list[ca_list_len-1].entries_size, ','); + } + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) { + append("ALL", &cm_list[cm_list_len].cmnd, + &cm_list[cm_list_len].cmnd_len, + &cm_list[cm_list_len].cmnd_size, 0); + expand_match_list(); + } + + cmnd_matches = TRUE; + $$ = TRUE; + } + | ALIAS { + if (printmatches == TRUE && in_alias == TRUE) { + append($1, &ca_list[ca_list_len-1].entries, + &ca_list[ca_list_len-1].entries_len, + &ca_list[ca_list_len-1].entries_size, ','); + } + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) { + append($1, &cm_list[cm_list_len].cmnd, + &cm_list[cm_list_len].cmnd_len, + &cm_list[cm_list_len].cmnd_size, 0); + expand_match_list(); + } + if (find_alias($1, CMND)) { + cmnd_matches = TRUE; + $$ = TRUE; + } + (void) free($1); + } + | COMMAND { + if (printmatches == TRUE && in_alias == TRUE) { + append($1.cmnd, &ca_list[ca_list_len-1].entries, + &ca_list[ca_list_len-1].entries_len, + &ca_list[ca_list_len-1].entries_size, ','); + if ($1.args) + append($1.args, &ca_list[ca_list_len-1].entries, + &ca_list[ca_list_len-1].entries_len, + &ca_list[ca_list_len-1].entries_size, ' '); + } + if (printmatches == TRUE && host_matches == TRUE && + user_matches == TRUE) { + append($1.cmnd, &cm_list[cm_list_len].cmnd, + &cm_list[cm_list_len].cmnd_len, + &cm_list[cm_list_len].cmnd_size, 0); + if ($1.args) + append($1.args, &cm_list[cm_list_len].cmnd, + &cm_list[cm_list_len].cmnd_len, + &cm_list[cm_list_len].cmnd_size, ' '); + expand_match_list(); + } + + /* if NewArgc > 1 pass ptr to 1st arg, else NULL */ + if (command_matches(cmnd, (NewArgc > 1) ? + cmnd_args : NULL, $1.cmnd, $1.args)) { + cmnd_matches = TRUE; + $$ = TRUE; + } + + (void) free($1.cmnd); + if ($1.args) + (void) free($1.args); + } + ; + +hostaliases : hostalias + | hostaliases ':' hostalias + ; + +hostalias : ALIAS { push; } '=' hostlist { + if (host_matches == TRUE && !add_alias($1, HOST)) + YYERROR; + pop; + } + ; + +hostlist : hostspec + | hostlist ',' hostspec + ; + +cmndaliases : cmndalias + | cmndaliases ':' cmndalias + ; + +cmndalias : ALIAS { + push; + if (printmatches == TRUE) { + in_alias = TRUE; + /* Allocate space for ca_list if necesary. */ + expand_ca_list(); + if (!(ca_list[ca_list_len-1].alias = strdup($1))){ + perror("malloc"); + (void) fprintf(stderr, + "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + } + } '=' cmndlist { + if (cmnd_matches == TRUE && !add_alias($1, CMND)) + YYERROR; + pop; + (void) free($1); + + if (printmatches == TRUE) + in_alias = FALSE; + } + ; + +cmndlist : cmnd + { ; } + | cmndlist ',' cmnd + ; + +useraliases : useralias + | useraliases ':' useralias + ; + +useralias : ALIAS { push; } '=' userlist { + if (user_matches == TRUE && !add_alias($1, USER)) + YYERROR; + pop; + (void) free($1); + } + ; + +userlist : user + { ; } + | userlist ',' user + ; + +user : NAME { + if (strcmp($1, user_name) == 0) + user_matches = TRUE; + (void) free($1); + } + | USERGROUP { + if (usergr_matches($1, user_name)) + user_matches = TRUE; + (void) free($1); + } + | NETGROUP { + if (netgr_matches($1, NULL, user_name)) + user_matches = TRUE; + (void) free($1); + } + | ALIAS { + /* could be an all-caps username */ + if (find_alias($1, USER) || !strcmp($1, user_name)) + user_matches = TRUE; + (void) free($1); + } + | ALL { + user_matches = TRUE; + } + ; + +%% + + +typedef struct { + int type; + char name[BUFSIZ]; +} aliasinfo; + +#define MOREALIASES (32) +aliasinfo *aliases = NULL; +size_t naliases = 0; +size_t nslots = 0; + + +/********************************************************************** + * + * aliascmp() + * + * This function compares two aliasinfo structures. + */ + +static int aliascmp(a1, a2) + const VOID *a1, *a2; +{ + int r; + aliasinfo *ai1, *ai2; + + ai1 = (aliasinfo *) a1; + ai2 = (aliasinfo *) a2; + r = strcmp(ai1->name, ai2->name); + if (r == 0) + r = ai1->type - ai2->type; + + return(r); +} + + +/********************************************************************** + * + * cmndaliascmp() + * + * This function compares two command_alias structures. + */ + +static int cmndaliascmp(entry, key) + const VOID *entry, *key; +{ + struct command_alias *ca1 = (struct command_alias *) key; + struct command_alias *ca2 = (struct command_alias *) entry; + + return(strcmp(ca1->alias, ca2->alias)); +} + + +/********************************************************************** + * + * add_alias() + * + * This function adds the named alias of the specified type to the + * aliases list. + */ + +static int add_alias(alias, type) + char *alias; + int type; +{ + aliasinfo ai, *aip; + char s[512]; + int ok; + + ok = FALSE; /* assume failure */ + ai.type = type; + (void) strcpy(ai.name, alias); + if (lfind((VOID *)&ai, (VOID *)aliases, &naliases, sizeof(ai), + aliascmp) != NULL) { + (void) sprintf(s, "Alias `%s' already defined", alias); + yyerror(s); + } else { + if (naliases == nslots && !more_aliases(nslots)) { + (void) sprintf(s, "Out of memory defining alias `%s'", alias); + yyerror(s); + } + + aip = (aliasinfo *) lsearch((VOID *)&ai, (VOID *)aliases, + &naliases, sizeof(ai), aliascmp); + + if (aip != NULL) { + ok = TRUE; + } else { + (void) sprintf(s, "Aliases corrupted defining alias `%s'", alias); + yyerror(s); + } + } + + return(ok); +} + + +/********************************************************************** + * + * find_alias() + * + * This function searches for the named alias of the specified type. + */ + +static int find_alias(alias, type) + char *alias; + int type; +{ + aliasinfo ai; + + (void) strcpy(ai.name, alias); + ai.type = type; + + return(lfind((VOID *)&ai, (VOID *)aliases, &naliases, + sizeof(ai), aliascmp) != NULL); +} + + +/********************************************************************** + * + * more_aliases() + * + * This function allocates more space for the aliases list. + */ + +static int more_aliases(nslots) + size_t nslots; +{ + aliasinfo *aip; + + if (nslots == 0) + aip = (aliasinfo *) malloc(MOREALIASES * sizeof(*aip)); + else + aip = (aliasinfo *) realloc(aliases, + (nslots + MOREALIASES) * sizeof(*aip)); + + if (aip != NULL) { + aliases = aip; + nslots += MOREALIASES; + } + + return(aip != NULL); +} + + +/********************************************************************** + * + * dumpaliases() + * + * This function lists the contents of the aliases list. + */ + +void dumpaliases() +{ + size_t n; + + for (n = 0; n < naliases; n++) { + switch (aliases[n].type) { + case HOST: + (void) puts("HOST"); + break; + + case CMND: + (void) puts("CMND"); + break; + + case USER: + (void) puts("USER"); + break; + } + (void) printf("\t%s\n", aliases[n].name); + } +} + + +/********************************************************************** + * + * list_matches() + * + * This function lists the contents of cm_list and ca_list for + * `sudo -l'. + */ + +void list_matches() +{ + int i; + char *p; + struct command_alias *ca, key; + + (void) puts("You may run the following commands on this host:"); + for (i = 0; i < cm_list_len; i++) { + + /* Print the runas list. */ + (void) fputs(" ", stdout); + if (cm_list[i].runas) { + (void) putchar('('); + if ((p = strtok(cm_list[i].runas, ":"))) + (void) fputs(p, stdout); + while ((p = strtok(NULL, ":"))) { + (void) fputs(", ", stdout); + (void) fputs(p, stdout); + } + (void) fputs(") ", stdout); + } else { + (void) fputs("(root) ", stdout); + } + + /* Is a password required? */ + if (cm_list[i].nopasswd == TRUE) + (void) fputs("NOPASSWD: ", stdout); + + /* Print the actual command or expanded Cmnd_Alias. */ + key.alias = cm_list[i].cmnd; + if ((ca = (struct command_alias *) lfind((VOID *) &key, + (VOID *) &ca_list[0], &ca_list_len, sizeof(key), cmndaliascmp))) + (void) puts(ca->entries); + else + (void) puts(cm_list[i].cmnd); + } + + /* Be nice and free up space now that we are done. */ + for (i = 0; i < ca_list_len; i++) { + (void) free(ca_list[i].alias); + (void) free(ca_list[i].entries); + } + (void) free(ca_list); + ca_list = NULL; + + for (i = 0; i < cm_list_len; i++) { + (void) free(cm_list[i].runas); + (void) free(cm_list[i].cmnd); + } + (void) free(cm_list); + cm_list = NULL; +} + + +/********************************************************************** + * + * append() + * + * This function appends a source string to the destination prefixing + * a separator if one is given. + */ + +static void append(src, dstp, dst_len, dst_size, separator) + char *src, **dstp; + size_t *dst_len, *dst_size; + int separator; +{ + /* Only add the separator if *dstp is non-NULL. */ + size_t src_len = strlen(src) + ((separator && *dstp) ? 1 : 0); + char *dst = *dstp; + + /* Assumes dst will be NULL if not set. */ + if (dst == NULL) { + if ((dst = (char *) malloc(BUFSIZ)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + *dst_size = BUFSIZ; + *dst_len = 0; + *dstp = dst; + } + + /* Allocate more space if necesary. */ + if (*dst_size <= *dst_len + src_len) { + while (*dst_size <= *dst_len + src_len) + *dst_size += BUFSIZ; + + if (!(dst = (char *) realloc(dst, *dst_size))) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + *dstp = dst; + } + + /* Copy src -> dst adding a separator char if appropriate and adjust len. */ + dst += *dst_len; + if (separator && *dst_len) + *dst++ = (char) separator; + (void) strcpy(dst, src); + *dst_len += src_len; +} + + +/********************************************************************** + * + * reset_aliases() + * + * This function frees up space used by the aliases list and resets + * the associated counters. + */ + +void reset_aliases() +{ + if (aliases) + (void) free(aliases); + naliases = nslots = 0; +} + + +/********************************************************************** + * + * expand_ca_list() + * + * This function increments ca_list_len, allocating more space as necesary. + */ + +static void expand_ca_list() +{ + if (++ca_list_len > ca_list_size) { + while ((ca_list_size += STACKINCREMENT) < ca_list_len); + if (ca_list == NULL) { + if ((ca_list = (struct command_alias *) + malloc(sizeof(struct command_alias) * ca_list_size)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + } else { + if ((ca_list = (struct command_alias *) realloc(ca_list, + sizeof(struct command_alias) * ca_list_size)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + } + } + + ca_list[ca_list_len - 1].entries = NULL; +} + + +/********************************************************************** + * + * expand_match_list() + * + * This function increments cm_list_len, allocating more space as necesary. + */ + +static void expand_match_list() +{ + if (++cm_list_len > cm_list_size) { + while ((cm_list_size += STACKINCREMENT) < cm_list_len); + if (cm_list == NULL) { + if ((cm_list = (struct command_match *) + malloc(sizeof(struct command_match) * cm_list_size)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + cm_list_len = 0; + } else { + if ((cm_list = (struct command_match *) realloc(cm_list, + sizeof(struct command_match) * cm_list_size)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + } + } + + cm_list[cm_list_len].runas = cm_list[cm_list_len].cmnd = NULL; + cm_list[cm_list_len].nopasswd = FALSE; +} + + +/********************************************************************** + * + * init_parser() + * + * This function frees up spaced used by a previous parse and + * allocates new space for various data structures. + */ + +void init_parser() +{ + /* Free up old data structures if we run the parser more than once. */ + if (match) { + (void) free(match); + match = NULL; + top = 0; + parse_error = FALSE; + errorlineno = -1; + sudolineno = 1; + } + + /* Allocate space for the matching stack. */ + stacksize = STACKINCREMENT; + match = (struct matchstack *) malloc(sizeof(struct matchstack) * stacksize); + if (match == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* Allocate space for the match list (for `sudo -l'). */ + if (printmatches == TRUE) + expand_match_list(); +} diff --git a/gnu/usr.bin/sudo/sudo/pathnames.h b/gnu/usr.bin/sudo/sudo/pathnames.h new file mode 100644 index 00000000000..0f4355ac896 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/pathnames.h @@ -0,0 +1,90 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: pathnames.h,v 1.1 1996/10/14 05:14:53 millert Exp $ + */ + +/* + * Pathnames to programs and files used by sudo. + */ + +#ifdef HAVE_PATHS_H +#include <paths.h> +#endif /* HAVE_PATHS_H */ + +#ifndef _PATH_DEV +#define _PATH_DEV "/dev/" +#endif /* _PATH_DEV */ + +/* + * NOTE: _PATH_SUDO_SUDOERS is usually overriden by the Makefile + */ +#ifndef _PATH_SUDO_SUDOERS +#define _PATH_SUDO_SUDOERS "/etc/sudoers" +#endif /* _PATH_SUDO_SUDOERS */ + +/* + * NOTE: _PATH_SUDO_STMP is usually overriden by the Makefile. + * _PATH_SUDO_STMP *MUST* be on the same partition + * as _PATH_SUDO_SUDOERS! + */ +#ifndef _PATH_SUDO_STMP +#define _PATH_SUDO_STMP "/etc/stmp" +#endif /* _PATH_SUDO_STMP */ + +#ifndef _PATH_SUDO_TIMEDIR +#define _PATH_SUDO_TIMEDIR _CONFIG_PATH_TIMEDIR +#endif /* _PATH_SUDO_TIMEDIR */ + +#ifndef _PATH_TTY +#define _PATH_TTY "/dev/tty" +#endif /* _PATH_TTY */ + +/* + * The following paths are gleaned via configure but you can override + * configure's values here if you want. + */ + +/* + * Where to put the sudo log file when logging to a file this + * is /var/log/sudo.log if /var/log exists, else /var/adm/sudo.log + */ +#ifndef _PATH_SUDO_LOGFILE +#define _PATH_SUDO_LOGFILE _CONFIG_PATH_LOGFILE +#endif /* _PATH_SUDO_LOGFILE */ + +#ifndef _PATH_SENDMAIL +#define _PATH_SENDMAIL _CONFIG_PATH_SENDMAIL +#endif /* _PATH_SENDMAIL */ + +#ifndef _PATH_VI +#define _PATH_VI _CONFIG_PATH_VI +#endif /* _PATH_VI */ + +#ifndef _PATH_PWD +#define _PATH_PWD _CONFIG_PATH_PWD +#endif /* _PATH_PWD */ + +#ifndef _PATH_MV +#define _PATH_MV _CONFIG_PATH_MV +#endif /* _PATH_MV */ + +#ifndef _PATH_BSHELL +#define _PATH_BSHELL _CONFIG_PATH_BSHELL +#endif /* _PATH_BSHELL */ diff --git a/gnu/usr.bin/sudo/sudo/sudo.8 b/gnu/usr.bin/sudo/sudo/sudo.8 new file mode 100644 index 00000000000..77e6fdc4525 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/sudo.8 @@ -0,0 +1,378 @@ +.rn '' }` +''' $RCSfile: sudo.8,v $$Revision: 1.1 $$Date: 1996/10/14 05:14:53 $ +''' +''' $Log: sudo.8,v $ +''' Revision 1.1 1996/10/14 05:14:53 millert +''' sudo 1.5.2 +''' +''' +.de Sh +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +.de Ip +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.de Vb +.ft CW +.nf +.ne \\$1 +.. +.de Ve +.ft R + +.fi +.. +''' +''' +''' Set up \*(-- to give an unbreakable dash; +''' string Tr holds user defined translation string. +''' Bell System Logo is used as a dummy character. +''' +.tr \(*W-|\(bv\*(Tr +.ie n \{\ +.ds -- \(*W- +.ds PI pi +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +.ds L' ' +.ds R' ' +'br\} +.el\{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds L' ` +.ds R' ' +.ds PI \(*p +'br\} +.\" If the F register is turned on, we'll generate +.\" index entries out stderr for the following things: +.\" TH Title +.\" SH Header +.\" Sh Subsection +.\" Ip Item +.\" X<> Xref (embedded +.\" Of course, you have to process the output yourself +.\" in some meaninful fashion. +.if \nF \{ +.de IX +.tm Index:\\$1\t\\n%\t"\\$2" +.. +.nr % 0 +.rr F +.\} +.TH sudo 8 "1.5.2" "6/Oct/96" "MAINTENANCE COMMANDS" +.IX Title "sudo 8" +.UC +.IX Name "sudo - execute a command as the superuser" +.if n .hy 0 +.if n .na +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.de CQ \" put $1 in typewriter font +.ft CW +'if n "\c +'if t \\&\\$1\c +'if n \\&\\$1\c +'if n \&" +\\&\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 +'.ft R +.. +.\" @(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2 +. \" AM - accent mark definitions +.bd B 3 +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds ? ? +. ds ! ! +. ds / +. ds q +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds ? \s-2c\h'-\w'c'u*7/10'\u\h'\*(#H'\zi\d\s+2\h'\w'c'u*8/10' +. ds ! \s-2\(or\s+2\h'-\w'\(or'u'\v'-.8m'.\v'.8m' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +. ds q o\h'-\w'o'u*8/10'\s-4\v'.4m'\z\(*i\v'-.4m'\s+4\h'\w'o'u*8/10' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds v \\k:\h'-(\\n(.wu*9/10-\*(#H)'\v'-\*(#V'\*(#[\s-4v\s0\v'\*(#V'\h'|\\n:u'\*(#] +.ds _ \\k:\h'-(\\n(.wu*9/10-\*(#H+(\*(#F*2/3))'\v'-.4m'\z\(hy\v'.4m'\h'|\\n:u' +.ds . \\k:\h'-(\\n(.wu*8/10)'\v'\*(#V*4/10'\z.\v'-\*(#V*4/10'\h'|\\n:u' +.ds 3 \*(#[\v'.2m'\s-2\&3\s0\v'-.2m'\*(#] +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +.ds oe o\h'-(\w'o'u*4/10)'e +.ds Oe O\h'-(\w'O'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds v \h'-1'\o'\(aa\(ga' +. ds _ \h'-1'^ +. ds . \h'-1'. +. ds 3 3 +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +. ds oe oe +. ds Oe OE +.\} +.rm #[ #] #H #V #F C +.SH "NAME" +.IX Header "NAME" +sudo \- execute a command as the superuser +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +\fBsudo\fR \fB\-V\fR | \fB\-h\fR | \fB\-l\fR | \fB\-v\fR | \fB\-k\fR | \fB\-s\fR | \fB\-H\fR | +[ \fB\-b\fR ] | [ \fB\-p\fR prompt ] [ \fB\-u\fR username/#uid] \fIcommand\fR +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +\fBsudo\fR allows a permitted user to execute a \fIcommand\fR +as the superuser (real and effective uid and gid are set +to \f(CW0\fR and root's group as set in the passwd file respectively). +.PP +\fBsudo\fR determines who is an authorized user by consulting the +file \fI/etc/sudoers\fR. By giving \fBsudo\fR the \f(CW-v\fR flag a user +can update the time stamp without running a \fIcommand.\fR +The password prompt itself will also time out if the password is +not entered with N minutes (again, this is defined at installation +time and defaults to 5 minutes). +.PP +If an unauthorized user executes \fBsudo\fR, mail will be sent from the +user to the local authorities (defined at installation time). +.PP +\fBsudo\fR was designed to log via the 4.3 BSD \fIsyslog\fR\|(3) facility but +can log to a file instead if so desired (or to both syslog and a file). +.PP +All preferences are defined at installation time and are derived from +the options.h and pathnames.h include files as well as as well as the +Makefile. +.SH "OPTIONS" +.IX Header "OPTIONS" +\fBsudo\fR accepts the following command line options: +.Ip "-V" 4 +.IX Item "-V" +The \f(CW-V\fR (\fIversion\fR) option causes \fBsudo\fR to print the +version number and exit. +.Ip "-l" 4 +.IX Item "-l" +The \f(CW-l\fR (\fIlist\fR) option will list out the allowed and +forbidden commands for the user on the current host. +.Ip "-h" 4 +.IX Item "-h" +The \f(CW-h\fR (\fIhelp\fR) option causes \fBsudo\fR to print the version +of \fBsudo\fR and a usage message before exiting. +.Ip "-v" 4 +.IX Item "-v" +If given the \f(CW-v\fR (\fIvalidate\fR) option, \fBsudo\fR will update the +user's timestamp file, prompting for a password if necessary. +This extends the \fBsudo\fR timeout to for another N minutes +(where N is defined at installation time and defaults to 5 +minutes) but does not run a command. +.Ip "-k" 4 +.IX Item "-k" +The \f(CW-k\fR (\fIkill\fR) option to \fBsudo\fR removes the user's timestamp +file, thus requiring a password the next time \fBsudo\fR is run. +This option does not require a password and was added to +allow a user to revoke \fBsudo\fR permissions from a .logout file. +.Ip "-b" 4 +.IX Item "-b" +The \f(CW-b\fR (\fIbackground\fR) option tells \fBsudo\fR to run the given +command in the background. Note that if you use the \f(CW-b\fR +option you cannot use shell job control to manipulate the command. +.Ip "-p" 4 +.IX Item "-p" +The \f(CW-p\fR (\fIprompt\fR) option allows you to override the default +password prompt and use a custom one. If the password prompt +contains the \f(CW%u\fR escape, \f(CW%u\fR will be replaced by the user's +login name. Similarly, \f(CW%h\fR will be replaced by the local +hostname. +.Ip "-u" 4 +.IX Item "-u" +The \f(CW-u\fR (\fIuser\fR) option causes sudo to run the specified command +as a user other than \fIroot\fR. To specify a \fIuid\fR instead of a +\fIusername\fR, use \*(L"#uid\*(R". +.Ip "-s" 4 +.IX Item "-s" +The \f(CW-s\fR (\fIshell\fR) option runs the shell specified by the \fI\s-1SHELL\s0\fR +environmental variable if it is set or the shell as specified +in \fIpasswd\fR\|(5). +.Ip "-H" 4 +.IX Item "-H" +The \f(CW-H\fR (\fI\s-1HOME\s0\fR) option sets the \fI\s-1HOME\s0\fR environmental variable +to the homedir of the target user (root by default) as specified +in \fIpasswd\fR\|(5). +.Ip "--" 4 +.IX Item "--" +The \f(CW--\fR flag indicates that \fBsudo\fR should stop processing command +line arguments. It is most useful in conjunction with the \f(CW-s\fR flag. +.SH "RETURN VALUES" +.IX Header "RETURN VALUES" +\fBsudo\fR quits with an exit value of 1 if there is a +configuration/permission problem or if \fBsudo\fR cannot execute +the given command. In the latter case the error string is +printed to stderr via \fIperror\fR\|(3). If \fBsudo\fR cannot \fIstat\fR\|(2) +one or more entries in the user's PATH the error is printed +on stderr via \fIperror\fR\|(3). (If the directory does not exist +or if it is not really a directory, the entry is ignored and +no error is printed.) This should not happen under normal +circumstances. The most common reason for \fIstat\fR\|(3) to return +\*(L"permission denied\*(R" is if you are running an automounter and +one of the directories in your PATH is on a machine that is +currently unreachable. +.SH "SECURITY NOTES" +.IX Header "SECURITY NOTES" +\fBsudo\fR tries to be safe when executing external commands. +Variables that control how dynamic loading and binding is +done can be used to subvert the program that \fBsudo\fR runs. +To combat this the \f(CWLD_*\fR, \f(CWSHLIB_PATH\fR (HP\-UX only), +\f(CWLIBPATH\fR (AIX only), and \f(CW_RLD_*\fR environmental variables are +removed from the environment passed on to all commands executed. +\fBsudo\fR will also remove the \f(CWIFS\fR, \f(CWENV\fR, \f(CWBASH_ENV\fR +and \f(CWKRB_CONF\fR variables as they too can pose a threat. +.PP +To prevent command spoofing, \fBsudo\fR checks "." and "" (both +denoting current directory) last when searching for a command +in the user's PATH (if one or both are in the PATH). +Note, however, that the actual PATH environmental variable +is \fInot\fR modified and is passed unchanged to the program that +\fBsudo\fR executes. +.PP +For security reasons, if your OS supports shared libraries, +\fBsudo\fR should always be statically linked unless the +dynamic loader disables user-defined library search paths +for setuid programs. (Most modern dynamic loaders do this.) +.PP +\fBsudo\fR will check the ownership of its timestamp directory +(\fI/var/run/sudo\fR or \fI/tmp/.odus\fR by default) and ignore +the directory's contents if it is not owned by root and +only read, writable, and executable by root. On systems +that allow users to give files away to root (via chown), +if the timestamp directory is located in a directory writable +by anyone (ie: \fI/tmp\fR), it is possible for a user to create +the timestamp directory before \fBsudo\fR is run. +However, because \fBsudo\fR checks the ownership and mode of +the directory, the only damage that can be done is to \*(L"hide\*(R" +files by putting them in the timestamp dir. This is unlikely +to happen since once the timestamp dir is owned by root and +inaccessible by any other user the user placing files there +would be unable to get them back out. To get around this +issue you can use a directory that is not world-writable +for the timestamps (\fI/var/adm/sudo\fR for instance). +.PP +\f(CWsudo\fR will not honor timestamp files set far in the +future. Timestamp files with a date greater than +current_time + 2 * \f(CWTIMEOUT\fR will be ignored and +sudo will log the anomaly. This is done to keep a user +from creating his/her own timestamp file with a bogus +date. +.SH "FILES" +.IX Header "FILES" +.PP +.Vb 1 +\& /etc/sudoers file of authorized users. +.Ve +.SH "ENVIRONMENT VARIABLES" +.IX Header "ENVIRONMENT VARIABLES" +.PP +.Vb 10 +\& PATH Set to a sane value if SECURE_PATH is set +\& SHELL Used to determine shell to run with -s option +\& HOME In -s mode, set to homedir of root (or runas user) +\& if built with the SHELL_SETS_HOME option +\& SUDO_PROMPT Replaces the default password prompt +\& SUDO_COMMAND Set to the command run by sudo +\& SUDO_USER Set to the login of the user who invoked sudo +\& SUDO_UID Set to the uid of the user who invoked sudo +\& SUDO_GID Set to the gid of the user who invoked sudo +\& SUDO_PS1 If set, PS1 will be set to its value +.Ve +.SH "AUTHORS" +.IX Header "AUTHORS" +Many people have worked on \fBsudo\fR over the years, this +version consists of code written primarily by: +.PP +.Vb 4 +\& Jeff Nieusma <nieusma@FirstLink.com> +\& David Hieb <davehieb@internetone.com> +\& Todd Miller <Todd.Miller@courtesan.com> +\& Chris Jepeway <jepeway@cs.utk.edu> +.Ve +See the HISTORY file in the \fBsudo\fR distribution for more details. +.PP +Please send all bugs, comments, and changes to sudo-bugs@courtesan.com. +.SH "DISCLAIMER" +.IX Header "DISCLAIMER" +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. +.PP +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., +675 Mass Ave, Cambridge, MA 02139, USA. +.SH "CAVEATS" +.IX Header "CAVEATS" +There is no easy way to prevent a user from gaining a root shell if +that user has access to commands allow shell escapes. +Running shell scripts via \fBsudo\fR can expose the same kernel bugs +that make setuid shell scripts unsafe on some operating systems. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\fIsudoers\fR\|(5), \fIvisudo\fR\|(8), \fIsu\fR\|(1). + +.rn }` '' diff --git a/gnu/usr.bin/sudo/sudo/sudo.c b/gnu/usr.bin/sudo/sudo/sudo.c new file mode 100644 index 00000000000..7e17f18c944 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/sudo.c @@ -0,0 +1,999 @@ +/* + * CU sudo version 1.5.2 (based on Root Group sudo version 1.1) + * + * This software comes with no waranty whatsoever, use at your own risk. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + */ + +/* + * sudo version 1.1 allows users to execute commands as root + * Copyright (C) 1991 The Root Group, Inc. + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + ************************************************************************** + * + * sudo.c + * + * This is the main() routine for sudo + * + * sudo is a program to allow users to execute commands + * as root. The commands are defined in a global network- + * wide file and can be distributed. + * + * sudo has been hacked far and wide. Too many people to + * know about. It's about time to come up with a secure + * version that will work well in a network. + * + * This most recent version is done by: + * + * Jeff Nieusma <nieusma@rootgroup.com> + * Dave Hieb <davehieb@rootgroup.com> + * + * However, due to the fact that both of the above are no longer + * working at Root Group, I am maintaining the "CU version" of + * sudo. + * Todd Miller <Todd.Miller@courtesan.com> + */ + +#ifndef lint +static char rcsid[] = "$Id: sudo.c,v 1.1 1996/10/14 05:14:54 millert Exp $"; +#endif /* lint */ + +#define MAIN + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#include <pwd.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/stat.h> +#include <sys/time.h> +#include <sys/param.h> +#include <netinet/in.h> +#include <netdb.h> +#if (SHADOW_TYPE == SPW_SECUREWARE) +# ifdef __hpux +# include <hpsecurity.h> +# else +# include <sys/security.h> +# endif /* __hpux */ +# include <prot.h> +#endif /* SPW_SECUREWARE */ +#ifdef HAVE_DCE +#include <pthread.h> +#endif /* HAVE_DCE */ + +#include "sudo.h" +#include <options.h> +#include "version.h" + +#ifndef STDC_HEADERS +#ifndef __GNUC__ /* gcc has its own malloc */ +extern char *malloc __P((size_t)); +#endif /* __GNUC__ */ +#ifdef HAVE_STRDUP +extern char *strdup __P((const char *)); +#endif /* HAVE_STRDUP */ +extern char *getenv __P((char *)); +#endif /* STDC_HEADERS */ + + +/* + * Local type declarations + */ +struct env_table { + char *name; + int len; +}; + + +/* + * Prototypes + */ +static int parse_args __P((void)); +static void usage __P((int)); +static void load_globals __P((int)); +static int check_sudoers __P((void)); +static void load_cmnd __P((int)); +static void add_env __P((int)); +static void clean_env __P((char **, struct env_table *)); +extern int user_is_exempt __P((void)); +extern struct passwd *sudo_getpwuid __P((uid_t)); +extern void list_matches __P((void)); + +/* + * Globals + */ +int Argc; +char **Argv; +int NewArgc = 0; +char **NewArgv = NULL; +struct passwd *user_pw_ent; +char *runas_user = "root"; +char *cmnd = NULL; +char *cmnd_args = NULL; +char *tty = NULL; +char *prompt; +char host[MAXHOSTNAMELEN + 1]; +char *shost; +char cwd[MAXPATHLEN + 1]; +struct stat cmnd_st; +static char *runas_homedir = NULL; +extern struct interface *interfaces; +extern int num_interfaces; +extern int printmatches; + +/* + * Table of "bad" envariables to remove and len for strncmp() + */ +struct env_table badenv_table[] = { + { "IFS=", 4 }, + { "LD_", 3 }, + { "_RLD_", 5 }, +#ifdef __hpux + { "SHLIB_PATH=", 11 }, +#endif /* __hpux */ +#ifdef _AIX + { "LIBPATH=", 8 }, +#endif /* _AIX */ +#ifdef HAVE_KERB4 + { "KRB_CONF", 8 }, +#endif + { "ENV=", 4 }, + { "BASH_ENV=", 9 }, + { (char *) NULL, 0 } +}; + + +/******************************************************************** + * + * main() + * + * the driving force behind sudo... + */ + +int main(argc, argv) + int argc; + char **argv; +{ + int rtn; + int sudo_mode = MODE_RUN; + extern char ** environ; + +#if (SHADOW_TYPE == SPW_SECUREWARE) + (void) set_auth_parameters(argc, argv); +#endif /* SPW_SECUREWARE */ + + Argv = argv; + Argc = argc; + + if (geteuid() != 0) { + (void) fprintf(stderr, "Sorry, %s must be setuid root.\n", Argv[0]); + exit(1); + } + + /* + * set the prompt based on $SUDO_PROMPT (can be overridden by `-p') + */ + if ((prompt = getenv("SUDO_PROMPT")) == NULL) + prompt = PASSPROMPT; + + /* + * parse our arguments + */ + sudo_mode = parse_args(); + + switch (sudo_mode) { + case MODE_VERSION: + case MODE_HELP: + (void) printf("CU Sudo version %s\n", version); + if (sudo_mode == MODE_VERSION) + exit(0); + else + usage(0); + break; + case MODE_VALIDATE: + cmnd = "validate"; + break; + case MODE_KILL: + cmnd = "kill"; + break; + case MODE_LIST: + cmnd = "list"; + printmatches = 1; + break; + } + + /* must have a command to run unless got -s */ + if (cmnd == NULL && NewArgc == 0 && !(sudo_mode & MODE_SHELL)) + usage(1); + + /* + * Close all file descriptors to make sure we have a nice + * clean slate from which to work. + */ +#ifdef HAVE_SYSCONF + for (rtn = sysconf(_SC_OPEN_MAX) - 1; rtn > 3; rtn--) + (void) close(rtn); +#else + for (rtn = getdtablesize() - 1; rtn > 3; rtn--) + (void) close(rtn); +#endif /* HAVE_SYSCONF */ + + clean_env(environ, badenv_table); + + load_globals(sudo_mode); /* load global variables used throughout sudo */ + + /* + * If we got the '-s' option (run shell) we need to redo NewArgv + * and NewArgc. This can only be done after load_globals(). + */ + if ((sudo_mode & MODE_SHELL)) { + char **dst, **src = NewArgv; + + NewArgv = (char **) malloc (sizeof(char *) * (++NewArgc + 1)); + if (NewArgv == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* add the shell as argv[0] */ + if (user_shell && *user_shell) { + if ((NewArgv[0] = strrchr(user_shell, '/') + 1) == (char *) 1) + NewArgv[0] = user_shell; + } else { + (void) fprintf(stderr, "%s: Unable to determine shell.", Argv[0]); + exit(1); + } + + /* copy the args from Argv */ + for (dst = NewArgv + 1; (*dst = *src) != NULL; ++src, ++dst) + ; + } + + rtn = check_sudoers(); /* check mode/owner on _PATH_SUDO_SUDOERS */ + if (rtn != ALL_SYSTEMS_GO) { + log_error(rtn); + set_perms(PERM_FULL_USER, sudo_mode); + inform_user(rtn); + exit(1); + } + +#ifdef SECURE_PATH + /* replace the PATH envariable with a secure one */ + if (!user_is_exempt() && sudo_setenv("PATH", SECURE_PATH)) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } +#endif /* SECURE_PATH */ + + if ((sudo_mode & MODE_RUN)) { + load_cmnd(sudo_mode); /* load the cmnd global variable */ + } else if (sudo_mode == MODE_KILL) { + remove_timestamp(); /* remove the timestamp ticket file */ + exit(0); + } + + add_env(!(sudo_mode & MODE_SHELL)); /* add in SUDO_* envariables */ + + /* validate the user but don't search for "validate" */ + rtn = validate((sudo_mode != MODE_VALIDATE && sudo_mode != MODE_LIST)); + + switch (rtn) { + + case VALIDATE_OK: + case VALIDATE_OK_NOPASS: + if (rtn != VALIDATE_OK_NOPASS) + check_user(); + log_error(ALL_SYSTEMS_GO); + if (sudo_mode == MODE_VALIDATE) + exit(0); + else if (sudo_mode == MODE_LIST) { + list_matches(); + exit(0); + } + + /* become specified user or root */ + set_perms(PERM_RUNAS, sudo_mode); + + /* set $HOME for `sudo -H' */ + if ((sudo_mode & MODE_RESET_HOME) && runas_homedir) + (void) sudo_setenv("HOME", runas_homedir); + +#ifndef PROFILING + if ((sudo_mode & MODE_BACKGROUND) && fork() > 0) { + exit(0); + } else { + /* + * Make sure we are not being spoofed. The stat should + * be cheap enough to make this almost bulletproof. + */ + if (cmnd_st.st_dev) { + struct stat st; + + if (stat(cmnd, &st) < 0) { + (void) fprintf(stderr, "%s: unable to stat %s: ", + Argv[0], cmnd); + perror(""); + exit(1); + } + + if (st.st_dev != cmnd_st.st_dev || + st.st_ino != cmnd_st.st_ino) { + /* log and send mail, then bitch */ + log_error(SPOOF_ATTEMPT); + inform_user(SPOOF_ATTEMPT); + exit(1); + } + } + EXEC(cmnd, NewArgv); /* run the command */ + } +#else + exit(0); +#endif /* PROFILING */ + /* + * If we got here then the exec() failed... + */ + (void) fprintf(stderr, "%s: ", Argv[0]); + perror(cmnd); + exit(-1); + break; + + default: + log_error(rtn); + set_perms(PERM_FULL_USER, sudo_mode); + inform_user(rtn); + exit(1); + break; + } +} + + + +/********************************************************************** + * + * load_globals() + * + * This function primes these important global variables: + * user_pw_ent, host, cwd, interfaces. + */ + +static void load_globals(sudo_mode) + int sudo_mode; +{ + char *p; +#ifdef FQDN + struct hostent *h_ent; +#endif /* FQDN */ + + /* + * Get a local copy of the user's struct passwd with the shadow password + * if necesary. It is assumed that euid is 0 at this point so we + * can read the shadow passwd file if necesary. + */ + user_pw_ent = sudo_getpwuid(getuid()); + set_perms(PERM_ROOT, sudo_mode); + set_perms(PERM_USER, sudo_mode); + if (user_pw_ent == NULL) { + /* need to make a fake user_pw_ent */ + struct passwd pw_ent; + char pw_name[MAX_UID_T_LEN+1]; + + /* fill in uid and name fields with the uid */ + pw_ent.pw_uid = getuid(); + (void) sprintf(pw_name, "%ld", (long) pw_ent.pw_uid); + pw_ent.pw_name = pw_name; + user_pw_ent = &pw_ent; + + /* complain, log, and die */ + log_error(GLOBAL_NO_PW_ENT); + inform_user(GLOBAL_NO_PW_ENT); + exit(1); + } + +#ifdef HAVE_TZSET + (void) tzset(); /* set the timezone if applicable */ +#endif /* HAVE_TZSET */ + + /* + * Need to get tty early since it's used for logging + */ + if ((tty = (char *) ttyname(0)) || (tty = (char *) ttyname(1))) { + if (strncmp(tty, _PATH_DEV, sizeof(_PATH_DEV) - 1) == 0) + tty += sizeof(_PATH_DEV) - 1; + if ((tty = (char *) strdup(tty)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + } else + tty = "none"; + +#ifdef UMASK + (void) umask((mode_t)UMASK); +#endif /* UMASK */ + +#ifdef NO_ROOT_SUDO + if (user_uid == 0) { + (void) fprintf(stderr, + "You are already root, you don't need to use sudo.\n"); + exit(1); + } +#endif + + /* + * so we know where we are... (do as user) + */ + if (!getwd(cwd)) { + /* try as root... */ + set_perms(PERM_ROOT, sudo_mode); + if (!getwd(cwd)) { + (void) fprintf(stderr, "%s: Can't get working directory!\n", + Argv[0]); + (void) strcpy(cwd, "unknown"); + } + set_perms(PERM_USER, sudo_mode); + } + + /* + * load the host global variable from gethostname() and use + * gethostbyname() if we want to be sure it is fully qualified. + */ + if ((gethostname(host, MAXHOSTNAMELEN))) { + strcpy(host, "localhost"); + log_error(GLOBAL_NO_HOSTNAME); + inform_user(GLOBAL_NO_HOSTNAME); + exit(2); + } +#ifdef FQDN + if ((h_ent = gethostbyname(host)) == NULL) + log_error(GLOBAL_HOST_UNREGISTERED); + else + strcpy(host, h_ent -> h_name); +#endif /* FQDN */ + + /* + * "host" is the (possibly fully-qualified) hostname and + * "shost" is the unqualified form of the hostname. + */ + if ((p = strchr(host, '.'))) { + *p = '\0'; + if ((shost = strdup(host)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + *p = '.'; + } else { + shost = &host[0]; + } + + /* + * load a list of ip addresses and netmasks into + * the interfaces array. + */ + load_interfaces(); +} + + + +/********************************************************************** + * + * parse_args() + * + * this function parses the arguments to sudo + */ + +static int parse_args() +{ + int ret = MODE_RUN; /* what mode is suod to be run in? */ + int excl = 0; /* exclusive arg, no others allowed */ + + NewArgv = Argv + 1; + NewArgc = Argc - 1; + +#ifdef SHELL_IF_NO_ARGS + if (Argc < 2) { /* no options and no command */ + ret |= MODE_SHELL; + return(ret); + } +#else + if (Argc < 2) /* no options and no command */ + usage(1); +#endif /* SHELL_IF_NO_ARGS */ + + while (NewArgc > 0 && NewArgv[0][0] == '-') { + if (NewArgv[0][1] != '\0' && NewArgv[0][2] != '\0') { + (void) fprintf(stderr, "%s: Please use single character options\n", + Argv[0]); + usage(1); + } + + if (excl) + usage(1); /* only one -? option allowed */ + + switch (NewArgv[0][1]) { + case 'p': + /* must have an associated prompt */ + if (NewArgv[1] == NULL) + usage(1); + + prompt = NewArgv[1]; + + /* shift Argv over and adjust Argc */ + NewArgc--; + NewArgv++; + break; + case 'u': + /* must have an associated runas user */ + if (NewArgv[1] == NULL) + usage(1); + + runas_user = NewArgv[1]; + + /* shift Argv over and adjust Argc */ + NewArgc--; + NewArgv++; + break; + case 'b': + ret |= MODE_BACKGROUND; + break; + case 'v': + ret = MODE_VALIDATE; + excl++; + break; + case 'k': + ret = MODE_KILL; + excl++; + break; + case 'l': + ret = MODE_LIST; + excl++; + break; + case 'V': + ret = MODE_VERSION; + excl++; + break; + case 'h': + ret = MODE_HELP; + excl++; + break; + case 's': + ret |= MODE_SHELL; +#ifdef SHELL_SETS_HOME + ret |= MODE_RESET_HOME; +#endif /* SHELL_SETS_HOME */ + break; + case 'H': + ret |= MODE_RESET_HOME; + break; + case '-': + NewArgc--; + NewArgv++; +#ifdef SHELL_IF_NO_ARGS + if (ret == MODE_RUN) + ret |= MODE_SHELL; +#endif /* SHELL_IF_NO_ARGS */ + return(ret); + case '\0': + (void) fprintf(stderr, "%s: '-' requires an argument\n", + Argv[0]); + usage(1); + default: + (void) fprintf(stderr, "%s: Illegal option %s\n", Argv[0], + NewArgv[0]); + usage(1); + } + NewArgc--; + NewArgv++; + } + + if (NewArgc > 0 && (ret == MODE_VALIDATE || ret == MODE_KILL || + ret == MODE_LIST)) + usage(1); + + return(ret); +} + + + +/********************************************************************** + * + * usage() + * + * this function just gives you instructions and exits + */ + +static void usage(exit_val) + int exit_val; +{ + (void) fprintf(stderr, "usage: %s -V | -h | -l | -v | -k | -H | [-b] [-p prompt] [-u username/#uid] -s | <command>\n", Argv[0]); + exit(exit_val); +} + + + +/********************************************************************** + * + * add_env() + * + * this function adds sudo-specific variables into the environment + */ + +static void add_env(contiguous) + int contiguous; +{ + char idstr[MAX_UID_T_LEN + 1]; + size_t size; + char *buf; + + /* add the SUDO_COMMAND envariable (cmnd + args) */ + size = strlen(cmnd) + 1; + if (NewArgc > 1) { + char *to, **from; + + if (contiguous) { + size += (size_t) NewArgv[NewArgc-1] + strlen(NewArgv[NewArgc-1]) - + (size_t) NewArgv[1] + 1; + } else { + for (from = &NewArgv[1]; *from; from++) + size += strlen(*from) + 1; + } + + if ((buf = (char *) malloc(size)) == NULL) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* + * Copy the command and it's arguments info buf + */ + (void) strcpy(buf, cmnd); + to = buf + strlen(cmnd); + for (from = &NewArgv[1]; *from; from++) { + *to++ = ' '; + (void) strcpy(to, *from); + to += strlen(*from); + } + } else { + buf = cmnd; + } + if (sudo_setenv("SUDO_COMMAND", buf)) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + if (NewArgc > 1) + (void) free(buf); + + /* grab a pointer to the flat arg string from the environment */ + if (NewArgc > 1 && (cmnd_args = getenv("SUDO_COMMAND"))) { + if ((cmnd_args = strchr(cmnd_args, ' '))) + cmnd_args++; + else + cmnd_args = NULL; + } + + /* add the SUDO_USER envariable */ + if (sudo_setenv("SUDO_USER", user_name)) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* add the SUDO_UID envariable */ + (void) sprintf(idstr, "%ld", (long) user_uid); + if (sudo_setenv("SUDO_UID", idstr)) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* add the SUDO_GID envariable */ + (void) sprintf(idstr, "%ld", (long) user_gid); + if (sudo_setenv("SUDO_GID", idstr)) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } + + /* set PS1 if SUDO_PS1 is set */ + if ((buf = getenv("SUDO_PS1"))) + if (sudo_setenv("PS1", buf)) { + perror("malloc"); + (void) fprintf(stderr, "%s: cannot allocate memory!\n", Argv[0]); + exit(1); + } +} + + + +/********************************************************************** + * + * load_cmnd() + * + * This function sets the cmnd global variable + */ + +static void load_cmnd(sudo_mode) + int sudo_mode; +{ + if (strlen(NewArgv[0]) > MAXPATHLEN) { + errno = ENAMETOOLONG; + (void) fprintf(stderr, "%s: %s: Pathname too long\n", Argv[0], + NewArgv[0]); + exit(1); + } + + /* + * Resolve the path + */ + if ((cmnd = find_path(NewArgv[0])) == NULL) { + (void) fprintf(stderr, "%s: %s: command not found\n", Argv[0], + NewArgv[0]); + exit(1); + } +} + + + +/********************************************************************** + * + * check_sudoers() + * + * This function check to see that the sudoers file is owned by + * uid SUDOERS_UID, gid SUDOERS_GID and is mode SUDOERS_MODE. + */ + +static int check_sudoers() +{ + struct stat statbuf; + int fd = -1; + char c; + int rtn = ALL_SYSTEMS_GO; + + /* + * Fix the mode and group on sudoers file from old default. + * Only works if filesystem is readable/writable by root. + */ + set_perms(PERM_ROOT, 0); + if (!lstat(_PATH_SUDO_SUDOERS, &statbuf) && SUDOERS_UID == statbuf.st_uid) { + if (SUDOERS_MODE != 0400 && (statbuf.st_mode & 0007777) == 0400) { + if (chmod(_PATH_SUDO_SUDOERS, SUDOERS_MODE) == 0) { + (void) fprintf(stderr, "%s: fixed mode on %s\n", + Argv[0], _PATH_SUDO_SUDOERS); + if (statbuf.st_gid != SUDOERS_GID) { + if (!chown(_PATH_SUDO_SUDOERS,GID_NO_CHANGE,SUDOERS_GID)) { + (void) fprintf(stderr, "%s: set group on %s\n", + Argv[0], _PATH_SUDO_SUDOERS); + statbuf.st_gid = SUDOERS_GID; + } else { + (void) fprintf(stderr,"%s: Unable to set group on %s: ", + Argv[0], _PATH_SUDO_SUDOERS); + perror(""); + } + } + } else { + (void) fprintf(stderr, "%s: Unable to fix mode on %s: ", + Argv[0], _PATH_SUDO_SUDOERS); + perror(""); + } + } + } + + set_perms(PERM_SUDOERS, 0); + + if ((fd = open(_PATH_SUDO_SUDOERS, O_RDONLY)) < 0 || read(fd, &c, 1) == -1) + rtn = NO_SUDOERS_FILE; + else if (lstat(_PATH_SUDO_SUDOERS, &statbuf)) + rtn = NO_SUDOERS_FILE; + else if (!S_ISREG(statbuf.st_mode)) + rtn = SUDOERS_NOT_FILE; + else if ((statbuf.st_mode & 0007777) != SUDOERS_MODE) + rtn = SUDOERS_WRONG_MODE; + else if (statbuf.st_uid != SUDOERS_UID || statbuf.st_gid != SUDOERS_GID) + rtn = SUDOERS_WRONG_OWNER; + + if (fd != -1) + (void) close(fd); + + set_perms(PERM_ROOT, 0); + set_perms(PERM_USER, 0); + + return(rtn); +} + + + +/********************************************************************** + * + * set_perms() + * + * this function sets real and effective uids and gids based on perm. + */ + +void set_perms(perm, sudo_mode) + int perm; + int sudo_mode; +{ + struct passwd *pw_ent; + + switch (perm) { + case PERM_ROOT: + if (setuid(0)) { + perror("setuid(0)"); + exit(1); + } + break; + + case PERM_USER: + (void) setgid(user_gid); + + if (seteuid(user_uid)) { + perror("seteuid(user_uid)"); + exit(1); + } + break; + + case PERM_FULL_USER: + if (setuid(0)) { + perror("setuid(0)"); + exit(1); + } + + (void) setgid(user_gid); + + if (setuid(user_uid)) { + perror("setuid(user_uid)"); + exit(1); + } + + break; + case PERM_RUNAS: + if (setuid(0)) { + perror("setuid(0)"); + exit(1); + } + + /* XXX - add group/gid support */ + if (*runas_user == '#') { + if (setuid(atoi(runas_user + 1))) { + (void) fprintf(stderr, + "%s: cannot set uid to %s: ", + Argv[0], runas_user); + perror(""); + exit(1); + } + } else { + if (!(pw_ent = getpwnam(runas_user))) { + (void) fprintf(stderr, + "%s: no passwd entry for %s!\n", + Argv[0], runas_user); + exit(1); + } + + if (setgid(pw_ent->pw_gid)) { + (void) fprintf(stderr, + "%s: cannot set gid to %d: ", + Argv[0], pw_ent->pw_gid); + perror(""); + exit(1); + } + + if (setuid(pw_ent->pw_uid)) { + (void) fprintf(stderr, + "%s: cannot set uid to %d: ", + Argv[0], pw_ent->pw_uid); + perror(""); + exit(1); + } + if (sudo_mode & MODE_RESET_HOME) + runas_homedir = pw_ent->pw_dir; + } + + break; + case PERM_SUDOERS: + if (setuid(0)) { + perror("setuid(0)"); + exit(1); + } + + if (setgid(SUDOERS_GID)) { + perror("setgid(SUDOERS_GID)"); + exit(1); + } + + /* + * If SUDOERS_UID == 0 we need to use + * a different uid in order to avoid + * NFS lossage. Using uid 1 is a bit + * bogus but should be safe. + */ + if (SUDOERS_UID == 0) { + if (seteuid(1)) { + perror("seteuid(1)"); + exit(1); + } + } else { + if (seteuid(SUDOERS_UID)) { + perror("seteuid(SUDOERS_UID)"); + exit(1); + } + } + + break; + } +} + + + +/********************************************************************** + * + * clean_env() + * + * This function removes things from the environment that match the + * entries in badenv_table. It would be nice to add in the SUDO_* + * variables here as well but cmnd has not been defined at this point. + */ + +static void clean_env(envp, badenv_table) + char **envp; + struct env_table *badenv_table; +{ + struct env_table *bad; + char **cur; + + /* + * Remove any envars that match entries in badenv_table + */ + for (cur = envp; *cur; cur++) { + for (bad = badenv_table; bad -> name; bad++) { + if (strncmp(*cur, bad -> name, bad -> len) == 0) { + /* got a match so remove it */ + char **move; + + for (move = cur; *move; move++) + *move = *(move + 1); + + cur--; + + break; + } + } + } +} diff --git a/gnu/usr.bin/sudo/sudo/sudo.h b/gnu/usr.bin/sudo/sudo/sudo.h new file mode 100644 index 00000000000..99e1f755255 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/sudo.h @@ -0,0 +1,236 @@ +/* + * CU sudo version 1.5.2 (based on Root Group sudo version 1.1) + * + * This software comes with no waranty whatsoever, use at your own risk. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + */ + +/* + * sudo version 1.1 allows users to execute commands as root + * Copyright (C) 1991 The Root Group, Inc. + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * $Id: sudo.h,v 1.1 1996/10/14 05:14:54 millert Exp $ + */ + +#ifndef _SUDO_SUDO_H +#define _SUDO_SUDO_H + +#include <pathnames.h> +#include "compat.h" + +/* + * IP address and netmask pairs for checking against local interfaces. + */ +struct interface { + struct in_addr addr; + struct in_addr netmask; +}; + +/* + * Data structure used in parsing sudoers; + * top of stack values are the ones that + * apply when parsing is done & can be + * accessed by *_matches macros + */ +#define STACKINCREMENT (32) +struct matchstack { + int user; + int cmnd; + int host; + int runas; + int nopass; +}; + +/* + * Data structure describing a command in the + * sudoers file. + */ +struct sudo_command { + char *cmnd; + char *args; +}; + + +extern struct matchstack *match; +extern int top; + +#define user_matches (match[top-1].user) +#define cmnd_matches (match[top-1].cmnd) +#define host_matches (match[top-1].host) +#define runas_matches (match[top-1].runas) +#define no_passwd (match[top-1].nopass) + +/* + * Structure containing command matches if "sudo -l" is used. + */ +struct command_match { + char *runas; + size_t runas_len; + size_t runas_size; + char *cmnd; + size_t cmnd_len; + size_t cmnd_size; + int nopasswd; +}; + +/* + * Structure containing Cmnd_Alias's if "sudo -l" is used. + */ +struct command_alias { + char *alias; + char *entries; + size_t entries_size; + size_t entries_len; +}; + +/* + * Maximum number of characters to log per entry. The syslogger + * will log this much, after that, it truncates the log line. + * We need this here to make sure that we continue with another + * syslog(3) call if the internal buffer is moe than 1023 characters. + */ +#ifndef MAXSYSLOGLEN +# define MAXSYSLOGLEN 960 +#endif + +#define SLOG_SYSLOG 0x01 +#define SLOG_FILE 0x02 +#define SLOG_BOTH 0x03 + +#define VALIDATE_OK 0x00 +#define VALIDATE_NO_USER 0x01 +#define VALIDATE_NOT_OK 0x02 +#define VALIDATE_OK_NOPASS 0x03 +#define VALIDATE_ERROR -1 + +/* + * the arguments passed to log_error() are ANDed with GLOBAL_PROBLEM + * If the result is TRUE, the argv is NOT logged with the error message + */ +#define GLOBAL_PROBLEM 0x20 +#define ALL_SYSTEMS_GO 0x00 +#define GLOBAL_NO_PW_ENT ( 0x01 | GLOBAL_PROBLEM ) +#define GLOBAL_NO_SPW_ENT ( 0x02 | GLOBAL_PROBLEM ) +#define GLOBAL_NO_HOSTNAME ( 0x03 | GLOBAL_PROBLEM ) +#define GLOBAL_HOST_UNREGISTERED ( 0x04 | GLOBAL_PROBLEM ) +#define PASSWORD_NOT_CORRECT 0x05 +#define PASSWORDS_NOT_CORRECT 0x06 +#define NO_SUDOERS_FILE ( 0x07 | GLOBAL_PROBLEM ) +#define BAD_SUDOERS_FILE ( 0x08 | GLOBAL_PROBLEM ) +#define SUDOERS_WRONG_OWNER ( 0x09 | GLOBAL_PROBLEM ) +#define SUDOERS_WRONG_MODE ( 0x0A | GLOBAL_PROBLEM ) +#define SUDOERS_NOT_FILE ( 0x0B | GLOBAL_PROBLEM ) +#define SPOOF_ATTEMPT 0x0D +#define BAD_STAMPDIR 0x0E +#define BAD_STAMPFILE 0x0F + +/* + * Boolean values + */ +#undef TRUE +#define TRUE 0x01 +#undef FALSE +#define FALSE 0x00 + +/* + * Various modes sudo can be in (based on arguments) in octal + */ +#define MODE_RUN 00001 +#define MODE_VALIDATE 00002 +#define MODE_KILL 00004 +#define MODE_VERSION 00010 +#define MODE_HELP 00020 +#define MODE_LIST 00040 +#define MODE_BACKGROUND 00100 +#define MODE_SHELL 00200 +#define MODE_RESET_HOME 00400 + +/* + * Used with set_perms() + */ +#define PERM_ROOT 0x00 +#define PERM_USER 0x01 +#define PERM_FULL_USER 0x02 +#define PERM_SUDOERS 0x03 +#define PERM_RUNAS 0x04 + +/* + * Shortcuts for user_pw_ent + */ +#define user_name (user_pw_ent -> pw_name) +#define user_passwd (user_pw_ent -> pw_passwd) +#define user_uid (user_pw_ent -> pw_uid) +#define user_gid (user_pw_ent -> pw_gid) +#define user_shell (user_pw_ent -> pw_shell) +#define user_dir (user_pw_ent -> pw_dir) + +/* + * Function prototypes + */ +#define YY_DECL int yylex __P((void)) + +#ifndef HAVE_STRDUP +char *strdup __P((const char *)); +#endif +#ifndef HAVE_GETWD +char *getwd __P((char *)); +#endif +#if !defined(HAVE_PUTENV) && !defined(HAVE_SETENV) +int putenv __P((const char *)); +#endif +char *sudo_goodpath __P((const char *)); +int sudo_setenv __P((char *, char *)); +char *tgetpass __P((char *, int, char *, char *)); +char * find_path __P((char *)); +void log_error __P((int)); +void inform_user __P((int)); +void check_user __P((void)); +int validate __P((int)); +void set_perms __P((int, int)); +void remove_timestamp __P((void)); +void load_interfaces __P((void)); +int yyparse __P((void)); +YY_DECL; + + +/* + * Most of these variables are declared in main() so they don't need + * to be extern'ed here if this is main... + */ +#ifndef MAIN +extern char host[]; +extern char *shost; +extern char cwd[]; +extern struct interface *interfaces; +extern int num_interfaces; +extern struct passwd *user_pw_ent; +extern char *runas_user; +extern char *tty; +extern char *cmnd; +extern char *cmnd_args; +extern char *prompt; +extern struct stat cmnd_st; +extern int Argc; +extern char **Argv; +extern int NewArgc; +extern char **NewArgv; +#endif +extern int errno; + +#endif /* _SUDO_SUDO_H */ diff --git a/gnu/usr.bin/sudo/sudo/sudo_setenv.c b/gnu/usr.bin/sudo/sudo/sudo_setenv.c new file mode 100644 index 00000000000..bcfca6be3df --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/sudo_setenv.c @@ -0,0 +1,94 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * This module contains sudo_setenv(). + * sudo_setenv(3) adds a string of the form "var=val" to the environment. + * + * Todd C. Miller (millert@colorado.edu) Fri Jun 3 18:32:19 MDT 1994 + */ + +#ifndef lint +static char rcsid[] = "$Id: sudo_setenv.c,v 1.1 1996/10/14 05:14:55 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#include <sys/types.h> +#include <sys/param.h> +#include <netinet/in.h> + +#include "sudo.h" +#include <options.h> + +#ifndef STDC_HEADERS +#ifdef HAVE_PUTENV +extern int putenv __P((const char *)); +#endif /* HAVE_PUTENV */ +#ifdef HAVE_SETENV +extern int setenv __P((char *, char *, int)); +#endif /* HAVE_SETENV */ +#endif /* !STDC_HEADERS */ + + +/********************************************************************** + * + * sudo_setenv() + * + * sudo_setenv() adds a string of the form "var=val" to the environment. + * If it is unable to expand the current environent it returns -1, + * else it returns 0. + */ + +int sudo_setenv(var, val) + char *var; + char *val; +{ + +#ifdef HAVE_SETENV + return(setenv(var, val, 1)); +#else + char *envstring, *tmp; + + envstring = tmp = (char *) malloc(strlen(var) + strlen(val) + 2); + if (envstring == NULL) + return(-1); + + while ((*tmp++ = *var++)) + ; + + *(tmp-1) = '='; + + while ((*tmp++ = *val++)) + ; + + return(putenv(envstring)); +#endif /* HAVE_SETENV */ +} diff --git a/gnu/usr.bin/sudo/sudo/sudoers.5 b/gnu/usr.bin/sudo/sudo/sudoers.5 new file mode 100644 index 00000000000..bcc6137b366 --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/sudoers.5 @@ -0,0 +1,435 @@ +.rn '' }` +''' $RCSfile: sudoers.5,v $$Revision: 1.1 $$Date: 1996/10/14 05:14:55 $ +''' +''' $Log: sudoers.5,v $ +''' Revision 1.1 1996/10/14 05:14:55 millert +''' sudo 1.5.2 +''' +''' +.de Sh +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +.de Ip +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.de Vb +.ft CW +.nf +.ne \\$1 +.. +.de Ve +.ft R + +.fi +.. +''' +''' +''' Set up \*(-- to give an unbreakable dash; +''' string Tr holds user defined translation string. +''' Bell System Logo is used as a dummy character. +''' +.tr \(*W-|\(bv\*(Tr +.ie n \{\ +.ds -- \(*W- +.ds PI pi +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +.ds L' ' +.ds R' ' +'br\} +.el\{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds L' ` +.ds R' ' +.ds PI \(*p +'br\} +.\" If the F register is turned on, we'll generate +.\" index entries out stderr for the following things: +.\" TH Title +.\" SH Header +.\" Sh Subsection +.\" Ip Item +.\" X<> Xref (embedded +.\" Of course, you have to process the output yourself +.\" in some meaninful fashion. +.if \nF \{ +.de IX +.tm Index:\\$1\t\\n%\t"\\$2" +.. +.nr % 0 +.rr F +.\} +.TH sudoers 5 "1.5.2" "7/Sep/96" "FILE FORMATS" +.IX Title "sudoers 5" +.UC +.IX Name "sudoers - list of which users may execute what as root" +.if n .hy 0 +.if n .na +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.de CQ \" put $1 in typewriter font +.ft CW +'if n "\c +'if t \\&\\$1\c +'if n \\&\\$1\c +'if n \&" +\\&\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 +'.ft R +.. +.\" @(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2 +. \" AM - accent mark definitions +.bd B 3 +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds ? ? +. ds ! ! +. ds / +. ds q +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds ? \s-2c\h'-\w'c'u*7/10'\u\h'\*(#H'\zi\d\s+2\h'\w'c'u*8/10' +. ds ! \s-2\(or\s+2\h'-\w'\(or'u'\v'-.8m'.\v'.8m' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +. ds q o\h'-\w'o'u*8/10'\s-4\v'.4m'\z\(*i\v'-.4m'\s+4\h'\w'o'u*8/10' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds v \\k:\h'-(\\n(.wu*9/10-\*(#H)'\v'-\*(#V'\*(#[\s-4v\s0\v'\*(#V'\h'|\\n:u'\*(#] +.ds _ \\k:\h'-(\\n(.wu*9/10-\*(#H+(\*(#F*2/3))'\v'-.4m'\z\(hy\v'.4m'\h'|\\n:u' +.ds . \\k:\h'-(\\n(.wu*8/10)'\v'\*(#V*4/10'\z.\v'-\*(#V*4/10'\h'|\\n:u' +.ds 3 \*(#[\v'.2m'\s-2\&3\s0\v'-.2m'\*(#] +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +.ds oe o\h'-(\w'o'u*4/10)'e +.ds Oe O\h'-(\w'O'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds v \h'-1'\o'\(aa\(ga' +. ds _ \h'-1'^ +. ds . \h'-1'. +. ds 3 3 +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +. ds oe oe +. ds Oe OE +.\} +.rm #[ #] #H #V #F C +.SH "NAME" +.IX Header "NAME" +sudoers \- list of which users may execute what as root +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +The \fIsudoers\fR file is composed of an optional host alias section, +an optional command alias section and the user specification section. +All command or host aliases need to start with their respective keywords +(ie: Host_Alias, User_Alias, or Cmnd_Alias). +If there are multiple occurrences of a user, the union of the entries +will be used. +.Sh "user specification format:" +.IX Subsection "user specification format:" +.PP +.Vb 1 +\& user access_group [: access_group] ... +.Ve +.Vb 10 +\& access_group ::= host_type = [(user_list)] [NOPASSWD:] [op]cmnd_type +\& [,[(user_list)] [NOPASSWD:] [op]cmnd_type] ... +\& host_type ::= a lower-case hostname, netgroup, ip address, +\& network number, network number/netmask, +\& or host alias. +\& user_list ::= comma-separated list of users, uids, or +\& User_Aliases the user may run commands as +\& (default is root). +\& cmnd_type ::= a command OR a command alias. +\& op ::= the logical "!" NOT operator. +.Ve +.Sh "host alias section format:" +.IX Subsection "host alias section format:" +.PP +.Vb 1 +\& Host_Alias HOSTALIAS = host-list +.Ve +.Vb 4 +\& Host_Alias ::= a keyword. +\& HOSTALIAS ::= an upper-case alias name. +\& host-list ::= a comma separated list of hosts, netgroups, +\& ip addresses, networks. +.Ve +.Sh "user alias section format:" +.IX Subsection "user alias section format:" +.PP +.Vb 1 +\& User_Alias USERALIAS = user-list +.Ve +.Vb 3 +\& User_Alias ::= a keyword. +\& USERALIAS ::= an upper-case alias name. +\& user-list ::= a comma separated list of users, groups, netgroups. +.Ve +.Sh "command alias section format:" +.IX Subsection "command alias section format:" +.PP +.Vb 1 +\& Cmnd_Alias CMNDALIAS = cmnd-list +.Ve +.Vb 3 +\& Cmnd_Alias ::= a keyword. +\& CMNDALIAS ::= an upper-case alias name. +\& cmnd-list ::= a comma separated list commands. +.Ve +.Sh "command specification:" +.IX Subsection "command specification:" +.PP +.Vb 1 +\& path arg1 arg2 .. argn = command +.Ve +.Vb 2 +\& path ::= a fully qualified pathname. +\& arg[1..n] ::= optional command line arguments. +.Ve +.Sh "wildcards (aka meta characters):" +.IX Subsection "wildcards (aka meta characters):" +\fBsudo\fR allows shell-style \fIwildcards\fR along with command arguments +in the \fIsudoers\fR file. Wildcard matching is done via the \fB\s-1POSIX\s0\fR +\f(CWfnmatch(3)\fR routine. +.Ip "\f(CW*\fR" 8 +.IX Item "\f(CW*\fR" +Matches any set of zero or more characters. +.Ip "\f(CW?\fR" 8 +.IX Item "\f(CW?\fR" +Matches any single character. +.Ip "\f(CW[...]\fR" 8 +.IX Item "\f(CW[...]\fR" +Matches any character in the specified range. +.Ip "\f(CW[!...]\fR" 8 +.IX Item "\f(CW[!...]\fR" +Matches any character \fBnot\fR in the specified range. +.Ip "\f(CW\ex\fR" 8 +.IX Item "\f(CW\ex\fR" +For any character \*(L"x\*(R", evaluates to \*(L"x\*(R". This is used to +escape special characters such as: \*(L"*\*(R", \*(L"?\*(R", \*(L"[\*(R", and \*(L"}\*(R". +.Sh "exceptions to wildcard rules:" +.IX Subsection "exceptions to wildcard rules:" +The following exceptions apply to the above rules: +.Ip "\f(CW""\fR" 8 +.IX Item "\f(CW""\fR" +If the empty string \f(CW""\fR is the only command line argument in the +\fIsudoers\fR entry it means that command may take \fBno\fR arguments. +.Sh "other special characters and reserved words:" +.IX Subsection "other special characters and reserved words:" +Text after a pound sign (\fB#\fR) is considered a comment. +Words that begin with a percent sign (\fB%\fR) are assumed to +be \s-1UN\s0*X groups (%staff refers to users in the group \fIstaff\fR). +Words that begin with a plus sign (\fB+\fR) are assumed to +be netgroups (\fB+cshosts\fR refers to the netgroup \fIcshosts\fR). +Long lines can be newline escaped with the backslash \fB\e\fR character. +The reserved word \fB\s-1NOPASSWD\s0\fR indicates that a user need not +enter a password for the command listed in that entry. +.PP +The reserved alias \fI\s-1ALL\s0\fR can be used for both {Host,User,Cmnd}_Alias. +\fB\s-1DO\s0 \s-1NOT\s0\fR define an alias of \fI\s-1ALL\s0\fR, it will \fB\s-1NOT\s0\fR be used. +Note that \fI\s-1ALL\s0\fR implies the entire universe of hosts/users/commands. +You can subtract elements from the universe by using the syntax: + user host=\s-1ALL\s0,!\s-1ALIAS1\s0,!/etc/halt... +Note that the \*(L"!\*(R" notation only works in a user's command list. You +may not use it to subtract elements in a User_Alias, Host_Alias, +Cmnd_Alias or user list. +.PP +Commands may have optional command line arguments. If they do, +then the arguments in the \fIsudoers\fR file must exactly match those +on the command line. It is also possible to have a command's +arguments span multiple lines as long as the line continuance +character \*(L"\e\*(R" is used. The following characters must be escaped +with a \*(L"\e\*(R" if used in command arguments: \*(L",\*(R", \*(L":\*(R", \*(L"=\*(R", \*(L"\e\*(R". +.SH "EXAMPLES" +.IX Header "EXAMPLES" +.PP +.Vb 7 +\& # Host alias specification +\& Host_Alias HUB=houdini:\e +\& REMOTE=merlin,kodiakthorn,spirit +\& Host_Alias SERVERS=houdini,merlin,kodiakthorn,spirit +\& Host_Alias CUNETS=128.138.0.0/255.255.0.0 +\& Host_Alias CSNETS=128.138.243.0,128.138.204.0,\e +\& 128.138.205.192 +.Ve +.Vb 3 +\& # User alias specification +\& User_Alias FULLTIME=millert,dowdy,mikef +\& User_Alias PARTTIME=juola,mccreary,tor +.Ve +.Vb 6 +\& # Command alias specification +\& Cmnd_Alias LPCS=/usr/etc/lpc,/usr/ucb/lprm +\& Cmnd_Alias SHELLS=/bin/sh,/bin/csh,/bin/tcsh,/bin/ksh +\& Cmnd_Alias SU=/bin/su +\& Cmnd_Alias MISC=/bin/rm,/bin/cat:\e +\& SHUTDOWN=/etc/halt,/etc/shutdown +.Ve +.Vb 14 +\& # User specification +\& FULLTIME ALL=(ALL) NOPASSWD: ALL +\& %wheel ALL=ALL +\& PARTTIME ALL=ALL,!SHELLS,!SU +\& +interns +openlabs=ALL,!SHELLS,!SU +\& britt REMOTE=SHUTDOWN:ALL=LPCS +\& jimbo CUNETS=/bin/su ?*,!/bin/su root +\& nieusma SERVERS=SHUTDOWN,/etc/reboot:\e +\& HUB=ALL,!SHELLS +\& jill houdini=/etc/shutdown -[hr] now,MISC +\& markm HUB=ALL,!MISC,!/etc/shutdown,!/etc/halt +\& davehieb merlin=ALL:SERVERS=/etc/halt:\e +\& kodiakthorn=NOPASSWD: ALL +\& steve CSNETS= (operator) /usr/op_commands/ +.Ve +.Sh "Host Alias specifications:" +.IX Subsection "Host Alias specifications:" +The are four \fIhost aliases\fR. The first actually contains +two \fIaliases\fR. It sets \f(CWHUB\fR to be \f(CWhoudini\fR and \f(CWREMOTE\fR +to the three machines \f(CWmerlin\fR, \f(CWkodiakthorn\fR and \f(CWspirit\fR. +Similarly, \f(CWSERVERS\fR is set to the machines \f(CWhoudini\fR, \f(CWmerlin\fR, +\f(CWkodiakthorn\fR and \f(CWspirit\fR. The \f(CWCSNETS\fR alias will match +any host on the 128.138.243.0, 128.138.204.0, or 128.138.205.192 +nets. The \f(CWCUNETS\fR alias will match any host on the 128.138.0.0 +(class B) network. Note that these are \fBnetwork\fR addresses, not ip +addresses. Unless an explicate netmask is given, the local \fInetmask\fR +is used to determine whether or not the current host belongs to a network. +.Sh "User Alias specifications:" +.IX Subsection "User Alias specifications:" +The two \fIuser aliases\fR simply groups the \f(CWFULLTIME\fR and +\f(CWPARTTIME\fR folks into two separate aliases. +.Sh "Command alias specifications:" +.IX Subsection "Command alias specifications:" +Command aliases are lists of commands with or without associated +command line arguments. The entries above should be self-explanatory. +.Sh "User specifications:" +.IX Subsection "User specifications:" +.Ip "\s-1FULLTIME\s0" 16 +.IX Item "\s-1FULLTIME\s0" +Full-time sysadmins in the \f(CWFULLTIME\fR alias may run any +command on any host as any user without a password. +.Ip "%wheel" 16 +.IX Item "%wheel" +Any user in the \s-1UN\s0*X group \f(CWwheel\fR may run any +command on any host. +.Ip "\s-1PARTTIME\s0" 16 +.IX Item "\s-1PARTTIME\s0" +Part-time sysadmins in the \f(CWPARTTIME\fR alias may run any +command except those in the \f(CWSHELLS\fR and \f(CWSU\fR aliases +on any host. +.Ip "+interns" 16 +.IX Item "+interns" +Any user in the netgroup \f(CWinterns\fR may run any +command except those in the \f(CWSHELLS\fR and \f(CWSU\fR aliases +on any host that is in the \f(CWopenlabs\fR netgroup. +.Ip "britt" 16 +.IX Item "britt" +The user \f(CWbritt\fR may run commands in the \f(CWSHUTDOWN\fR alias +on the \f(CWREMOTE\fR machines and commands in the \f(CWLPCS\fR alias +on any machine. +.Ip "jimbo" 16 +.IX Item "jimbo" +The user \f(CWjimbo\fR may \f(CWsu\fR to any user save root on the +machines on \f(CWCUNETS\fR (which is explicately listed as a class +B network). +.Ip "nieusma" 16 +.IX Item "nieusma" +The user \f(CWnieusma\fR may run commands in the \f(CWSHUTDOWN\fR alias +as well as \fI/etc/reboot\fR on the \f(CWSERVER\fR machines and +any command except those in the \f(CWSHELLS\fR alias on the \f(CWHUB\fR +machines. +.Ip "jill" 16 +.IX Item "jill" +The user \f(CWjill\fR may run \f(CW/etc/shutdown -h now\fR or +\f(CW/etc/shutdown -r now\fR as well as the commands in the +\f(CWMISC\fR alias on houdini. +.Ip "markm" 16 +.IX Item "markm" +The user \f(CWmarkm\fR may run any command on the \f(CWHUB\fR machines +except \fI/etc/shutdown\fR, \fI/etc/halt\fR, and commands listed +in the \f(CWMISC\fR alias. +.Ip "davehieb" 16 +.IX Item "davehieb" +The user \f(CWdavehieb\fR may run any command on \f(CWmerlin\fR, +\fI/etc/halt\fR on the \f(CWSERVERS\fR. He may also run any command +on \f(CWkodiakthorn\fR without giving a password. +.Ip "steve" 16 +.IX Item "steve" +The user \f(CWsteve\fR may run any command in the \fI/usr/op_commands/\fR +directory as user \f(CWoperator\fR on the machines on \f(CWCSNETS\fR. +.SH "CAVEATS" +.IX Header "CAVEATS" +The \fIsudoers\fR file should \fBalways\fR be edited by the \fBvisudo\fR +command which locks the file and does grammatical checking. It is +imperative that the \fIsudoers\fR be free of syntax errors since sudo +will not run with a syntactically incorrect \fIsudoers\fR file. +.SH "FILES" +.IX Header "FILES" +.PP +.Vb 2 +\& /etc/sudoers file of authorized users. +\& /etc/netgroup list of network groups. +.Ve +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\fIsudo\fR\|(8), \fIvisudo\fR\|(8), \fIsu\fR\|(1), \fIfnmatch\fR\|(3). + +.rn }` '' diff --git a/gnu/usr.bin/sudo/sudo/tgetpass.c b/gnu/usr.bin/sudo/sudo/tgetpass.c new file mode 100644 index 00000000000..b8a198d7adc --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/tgetpass.c @@ -0,0 +1,275 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * This module contains tgetpass(), getpass(3) with a timeout. + * It should work on any OS that supports sgtty (4BSD), termio (SYSV), + * or termios (POSIX) line disciplines. + * + * Todd C. Miller Sun Jun 5 17:22:31 MDT 1994 + */ + +#ifndef lint +static char rcsid[] = "$Id: tgetpass.c,v 1.1 1996/10/14 05:14:56 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#include <limits.h> +#include <pwd.h> +#include <sys/types.h> +#ifdef HAVE_SYS_BSDTYPES_H +#include <sys/bsdtypes.h> +#endif /* HAVE_SYS_BSDTYPES_H */ +#ifdef HAVE_SYS_SELECT_H +#include <sys/select.h> +#endif /* HAVE_SYS_SELECT_H */ +#include <sys/time.h> +#include <signal.h> +#include <fcntl.h> +#ifdef HAVE_TERMIOS_H +#include <termios.h> +#else +#ifdef HAVE_TERMIO_H +#include <termio.h> +#else +#include <sgtty.h> +#include <sys/ioctl.h> +#endif /* HAVE_TERMIO_H */ +#endif /* HAVE_TERMIOS_H */ +#if (SHADOW_TYPE == SPW_SECUREWARE) +# ifdef __hpux +# include <hpsecurity.h> +# else +# include <sys/security.h> +# endif /* __hpux */ +# include <prot.h> +#endif /* SPW_SECUREWARE */ + +#include <pathnames.h> +#include "compat.h" + +#ifndef TCSASOFT +#define TCSASOFT 0 +#endif /* TCSASOFT */ + + +/****************************************************************** + * + * tgetpass() + * + * this function prints a prompt and gets a password from /dev/tty + * or stdin. Echo is turned off (if possible) during password entry + * and input will time out based on the value of timeout. + */ + +char * tgetpass(prompt, timeout, user, host) + const char *prompt; + int timeout; + char *user; + char *host; +{ +#ifdef HAVE_TERMIOS_H + struct termios term; +#else +#ifdef HAVE_TERMIO_H + struct termio term; +#else + struct sgttyb ttyb; +#endif /* HAVE_TERMIO_H */ +#endif /* HAVE_TERMIOS_H */ +#ifdef POSIX_SIGNALS + sigset_t oldmask; + sigset_t mask; +#else + int oldmask; +#endif /* POSIX_SIGNALS */ + int n, echo; + FILE *input, *output; + static char buf[_PASSWD_LEN + 1]; + fd_set readfds; + struct timeval tv; + char *p; + + /* + * mask out SIGINT and SIGTSTP, should probably just catch and deal. + */ +#ifdef POSIX_SIGNALS + (void) sigemptyset(&mask); + (void) sigaddset(&mask, SIGINT); + (void) sigaddset(&mask, SIGTSTP); + (void) sigprocmask(SIG_BLOCK, &mask, &oldmask); +#else + oldmask = sigblock(sigmask(SIGINT)|sigmask(SIGTSTP)); +#endif + + /* + * open /dev/tty for reading/writing if possible or use + * stdin and stderr instead. + */ + if ((input = fopen(_PATH_TTY, "r+")) == NULL) { + input = stdin; + output = stderr; + (void) fflush(output); + } else { + output = input; + } + + /* + * turn off echo + */ +#ifdef HAVE_TERMIOS_H + (void) tcgetattr(fileno(input), &term); + if ((echo = (term.c_lflag & ECHO))) { + term.c_lflag &= ~ECHO; + (void) tcsetattr(fileno(input), TCSAFLUSH|TCSASOFT, &term); + } +#else +#ifdef HAVE_TERMIO_H + (void) ioctl(fileno(input), TCGETA, &term); + if ((echo = (term.c_lflag & ECHO))) { + term.c_lflag &= ~ECHO; + (void) ioctl(fileno(input), TCSETA, &term); + } +#else + (void) ioctl(fileno(input), TIOCGETP, &ttyb); + if ((echo = (ttyb.sg_flags & ECHO))) { + ttyb.sg_flags &= ~ECHO; + (void) ioctl(fileno(input), TIOCSETP, &ttyb); + } +#endif /* HAVE_TERMIO_H */ +#endif /* HAVE_TERMIOS_H */ + + /* print the prompt */ + if (prompt) { + p = (char *) prompt; + do { + /* expand %u -> username, %h -> host */ + switch (*p) { + case '%': if (user && *(p+1) == 'u') { + (void) fputs(user, output); + p++; + break; + } else if (host && *(p+1) == 'h') { + (void) fputs(host, output); + p++; + break; + } + + default: (void) fputc(*p, output); + } + } while (*(++p)); + } + + /* rewind if necesary */ + if (input == output) { + (void) fflush(output); + (void) rewind(output); + } + + /* + * Timeout of <= 0 means no timeout + */ + if (timeout > 0) { + /* setup for select(2) */ + FD_ZERO(&readfds); + FD_SET(fileno(input), &readfds); + + /* set timeout for select */ + tv.tv_sec = timeout; + tv.tv_usec = 0; + + /* how many file descriptors may we have? */ +#ifdef HAVE_SYSCONF + n = sysconf(_SC_OPEN_MAX); +#else + n = getdtablesize(); +#endif /* HAVE_SYSCONF */ + + /* + * get password or return empty string if nothing to read by timeout + */ + buf[0] = '\0'; + if (select(n, &readfds, 0, 0, &tv) > 0 && fgets(buf, sizeof(buf), input)) { + n = strlen(buf); + if (buf[n - 1] == '\n') + buf[n - 1] = '\0'; + } + } else { + buf[0] = '\0'; + if (fgets(buf, sizeof(buf), input)) { + n = strlen(buf); + if (buf[n - 1] == '\n') + buf[n - 1] = '\0'; + } + } + + /* turn on echo */ +#ifdef HAVE_TERMIOS_H + if (echo) { + term.c_lflag |= ECHO; + (void) tcsetattr(fileno(input), TCSAFLUSH|TCSASOFT, &term); + } +#else +#ifdef HAVE_TERMIO_H + if (echo) { + term.c_lflag |= ECHO; + (void) ioctl(fileno(input), TCSETA, &term); + } +#else + if (echo) { + ttyb.sg_flags |= ECHO; + (void) ioctl(fileno(input), TIOCSETP, &ttyb); + } +#endif /* HAVE_TERMIO_H */ +#endif /* HAVE_TERMIOS_H */ + + /* rewind if necesary */ + if (input == output) { + (void) fflush(output); + (void) rewind(output); + } + + /* print a newline since echo is turned off */ + (void) fputc('\n', output); + + /* restore old signal mask */ +#ifdef POSIX_SIGNALS + (void) sigprocmask(SIG_SETMASK, &oldmask, NULL); +#else + (void) sigsetmask(oldmask); +#endif + + /* close /dev/tty if that's what we opened */ + if (input != stdin) + (void) fclose(input); + + return(buf); +} diff --git a/gnu/usr.bin/sudo/sudo/version.h b/gnu/usr.bin/sudo/sudo/version.h new file mode 100644 index 00000000000..6cb682af53c --- /dev/null +++ b/gnu/usr.bin/sudo/sudo/version.h @@ -0,0 +1,28 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + * $Id: version.h,v 1.1 1996/10/14 05:14:56 millert Exp $ + */ + +#ifndef _SUDO_VERSION_H +#define _SUDO_VERSION_H + +static char version[] = "1.5.2"; + +#endif /* _SUDO_VERSION_H */ diff --git a/gnu/usr.bin/sudo/visudo/Makefile b/gnu/usr.bin/sudo/visudo/Makefile new file mode 100644 index 00000000000..c19e408ed38 --- /dev/null +++ b/gnu/usr.bin/sudo/visudo/Makefile @@ -0,0 +1,23 @@ +# $OpenBSD: Makefile,v 1.1 1996/10/14 05:14:57 millert Exp $ + +PROG= visudo +MAN= visudo.8 +CFLAGS+=-I${.CURDIR}/../sudo -I. +SRCS= y.tab.c lex.yy.c visudo.c +CLEANFILES+=y.tab.c y.tab.h lex.yy.c + +LDADD= -lcompat +DPADD= ${LIBCOMPAT} + +BINOWN= root +BINMODE=111 +BINDIR?=/usr/sbin + +.include <bsd.prog.mk> + +lex.yy.c: ${.CURDIR}/../sudo/parse.lex + rm -f lex.yy.c + $(LEX) ${.CURDIR}/../sudo/parse.lex + +y.tab.c y.tab.h: ${.CURDIR}/../sudo/parse.yacc + $(YACC) -d ${.CURDIR}/../sudo/parse.yacc diff --git a/gnu/usr.bin/sudo/visudo/visudo.8 b/gnu/usr.bin/sudo/visudo/visudo.8 new file mode 100644 index 00000000000..1a976177f42 --- /dev/null +++ b/gnu/usr.bin/sudo/visudo/visudo.8 @@ -0,0 +1,266 @@ +.rn '' }` +''' $RCSfile: visudo.8,v $$Revision: 1.1 $$Date: 1996/10/14 05:14:58 $ +''' +''' $Log: visudo.8,v $ +''' Revision 1.1 1996/10/14 05:14:58 millert +''' sudo 1.5.2 +''' +''' +.de Sh +.br +.if t .Sp +.ne 5 +.PP +\fB\\$1\fR +.PP +.. +.de Sp +.if t .sp .5v +.if n .sp +.. +.de Ip +.br +.ie \\n(.$>=3 .ne \\$3 +.el .ne 3 +.IP "\\$1" \\$2 +.. +.de Vb +.ft CW +.nf +.ne \\$1 +.. +.de Ve +.ft R + +.fi +.. +''' +''' +''' Set up \*(-- to give an unbreakable dash; +''' string Tr holds user defined translation string. +''' Bell System Logo is used as a dummy character. +''' +.tr \(*W-|\(bv\*(Tr +.ie n \{\ +.ds -- \(*W- +.ds PI pi +.if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch +.if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch +.ds L" "" +.ds R" "" +.ds L' ' +.ds R' ' +'br\} +.el\{\ +.ds -- \(em\| +.tr \*(Tr +.ds L" `` +.ds R" '' +.ds L' ` +.ds R' ' +.ds PI \(*p +'br\} +.\" If the F register is turned on, we'll generate +.\" index entries out stderr for the following things: +.\" TH Title +.\" SH Header +.\" Sh Subsection +.\" Ip Item +.\" X<> Xref (embedded +.\" Of course, you have to process the output yourself +.\" in some meaninful fashion. +.if \nF \{ +.de IX +.tm Index:\\$1\t\\n%\t"\\$2" +.. +.nr % 0 +.rr F +.\} +.TH visudo 8 "1.5.2" "7/Sep/96" "MAINTENANCE COMMANDS" +.IX Title "visudo 8" +.UC +.IX Name "visudo - edit the sudoers file" +.if n .hy 0 +.if n .na +.ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' +.de CQ \" put $1 in typewriter font +.ft CW +'if n "\c +'if t \\&\\$1\c +'if n \\&\\$1\c +'if n \&" +\\&\\$2 \\$3 \\$4 \\$5 \\$6 \\$7 +'.ft R +.. +.\" @(#)ms.acc 1.5 88/02/08 SMI; from UCB 4.2 +. \" AM - accent mark definitions +.bd B 3 +. \" fudge factors for nroff and troff +.if n \{\ +. ds #H 0 +. ds #V .8m +. ds #F .3m +. ds #[ \f1 +. ds #] \fP +.\} +.if t \{\ +. ds #H ((1u-(\\\\n(.fu%2u))*.13m) +. ds #V .6m +. ds #F 0 +. ds #[ \& +. ds #] \& +.\} +. \" simple accents for nroff and troff +.if n \{\ +. ds ' \& +. ds ` \& +. ds ^ \& +. ds , \& +. ds ~ ~ +. ds ? ? +. ds ! ! +. ds / +. ds q +.\} +.if t \{\ +. ds ' \\k:\h'-(\\n(.wu*8/10-\*(#H)'\'\h"|\\n:u" +. ds ` \\k:\h'-(\\n(.wu*8/10-\*(#H)'\`\h'|\\n:u' +. ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'^\h'|\\n:u' +. ds , \\k:\h'-(\\n(.wu*8/10)',\h'|\\n:u' +. ds ~ \\k:\h'-(\\n(.wu-\*(#H-.1m)'~\h'|\\n:u' +. ds ? \s-2c\h'-\w'c'u*7/10'\u\h'\*(#H'\zi\d\s+2\h'\w'c'u*8/10' +. ds ! \s-2\(or\s+2\h'-\w'\(or'u'\v'-.8m'.\v'.8m' +. ds / \\k:\h'-(\\n(.wu*8/10-\*(#H)'\z\(sl\h'|\\n:u' +. ds q o\h'-\w'o'u*8/10'\s-4\v'.4m'\z\(*i\v'-.4m'\s+4\h'\w'o'u*8/10' +.\} +. \" troff and (daisy-wheel) nroff accents +.ds : \\k:\h'-(\\n(.wu*8/10-\*(#H+.1m+\*(#F)'\v'-\*(#V'\z.\h'.2m+\*(#F'.\h'|\\n:u'\v'\*(#V' +.ds 8 \h'\*(#H'\(*b\h'-\*(#H' +.ds v \\k:\h'-(\\n(.wu*9/10-\*(#H)'\v'-\*(#V'\*(#[\s-4v\s0\v'\*(#V'\h'|\\n:u'\*(#] +.ds _ \\k:\h'-(\\n(.wu*9/10-\*(#H+(\*(#F*2/3))'\v'-.4m'\z\(hy\v'.4m'\h'|\\n:u' +.ds . \\k:\h'-(\\n(.wu*8/10)'\v'\*(#V*4/10'\z.\v'-\*(#V*4/10'\h'|\\n:u' +.ds 3 \*(#[\v'.2m'\s-2\&3\s0\v'-.2m'\*(#] +.ds o \\k:\h'-(\\n(.wu+\w'\(de'u-\*(#H)/2u'\v'-.3n'\*(#[\z\(de\v'.3n'\h'|\\n:u'\*(#] +.ds d- \h'\*(#H'\(pd\h'-\w'~'u'\v'-.25m'\f2\(hy\fP\v'.25m'\h'-\*(#H' +.ds D- D\\k:\h'-\w'D'u'\v'-.11m'\z\(hy\v'.11m'\h'|\\n:u' +.ds th \*(#[\v'.3m'\s+1I\s-1\v'-.3m'\h'-(\w'I'u*2/3)'\s-1o\s+1\*(#] +.ds Th \*(#[\s+2I\s-2\h'-\w'I'u*3/5'\v'-.3m'o\v'.3m'\*(#] +.ds ae a\h'-(\w'a'u*4/10)'e +.ds Ae A\h'-(\w'A'u*4/10)'E +.ds oe o\h'-(\w'o'u*4/10)'e +.ds Oe O\h'-(\w'O'u*4/10)'E +. \" corrections for vroff +.if v .ds ~ \\k:\h'-(\\n(.wu*9/10-\*(#H)'\s-2\u~\d\s+2\h'|\\n:u' +.if v .ds ^ \\k:\h'-(\\n(.wu*10/11-\*(#H)'\v'-.4m'^\v'.4m'\h'|\\n:u' +. \" for low resolution devices (crt and lpr) +.if \n(.H>23 .if \n(.V>19 \ +\{\ +. ds : e +. ds 8 ss +. ds v \h'-1'\o'\(aa\(ga' +. ds _ \h'-1'^ +. ds . \h'-1'. +. ds 3 3 +. ds o a +. ds d- d\h'-1'\(ga +. ds D- D\h'-1'\(hy +. ds th \o'bp' +. ds Th \o'LP' +. ds ae ae +. ds Ae AE +. ds oe oe +. ds Oe OE +.\} +.rm #[ #] #H #V #F C +.SH "NAME" +.IX Header "NAME" +visudo \- edit the sudoers file +.SH "SYNOPSIS" +.IX Header "SYNOPSIS" +\fBvisudo\fR [ \fB\-V\fR ] +.SH "DESCRIPTION" +.IX Header "DESCRIPTION" +\fBvisudo\fR edits the \fIsudoers\fR file in a safe fashion, analogous to +\fIvipw\fR\|(8). \fBvisudo\fR locks the \fIsudoers\fR file against multiple +simultaneous edits, provides basic sanity checks, and checks +for parse errors. If the \fIsudoers\fR file is currently being +edited you will receive a message to try again later. In the +default configuration, the \fIvi\fR\|(1) editor is used, but there is +a compile time option to allow use of whatever editor the +environmental variables \f(CWEDITOR\fR or \f(CWVISUAL\fR are set to. +.PP +\fBvisudo\fR parses the \fIsudoers\fR file after the edit and will +not save the changes if there is a syntax error. Upon finding +an error, a message will be printed stating the line \fInumber\fR\|(s) +that the error occurred on and the user will receive the +\*(L"What now?\*(R" prompt. At this point the user may enter \*(L"e\*(R" +to re-edit the \fIsudoers\fR file, enter \*(L"x\*(R" to exit without +saving the changes, or \*(L"q\*(R" to quit and save changes. The +\*(L"q\*(R" option should be used with extreme care because if \fBvisudo\fR +believes there to be a parse error, so will \fBsudo\fR and no one +will be able to execute \fBsudo\fR again until the error is fixed. +Any other command at this prompt will print a short help message. +When editing the \fIsudoers\fR file after a parse error has been +detected the cursor will be placed on the line where the error +occurred (if the editor supports this feature). +.SH "OPTIONS" +.IX Header "OPTIONS" +\fBvisudo\fR accepts the following command line option: +.Ip "-V" 4 +.IX Item "-V" +The \f(CW-V\fR (version) option causes \fBvisudo\fR to print the version number +and exit. +.SH "FILES" +.IX Header "FILES" +.PP +.Vb 2 +\& /etc/sudoers file of authorized users. +\& /etc/stmp lock file for visudo. +.Ve +.SH "ENVIRONMENT VARIABLES" +.IX Header "ENVIRONMENT VARIABLES" +The following are used only if \fBvisudo\fR was compiled with the +\fIENV_EDITOR\fR option: +.PP +.Vb 2 +\& EDITOR Used by visudo as the editor to use. +\& VISUAL Used by visudo if EDITOR is not set. +.Ve +.SH "AUTHOR" +.IX Header "AUTHOR" +Many people have worked on \fIsudo\fR over the years, this version of +\fBvisudo\fR was written by: +.PP +.Vb 1 +\& Todd Miller <Todd.Miller@courtesan.com> +.Ve +See the HISTORY file in the sudo distribution for more details. +.PP +Please send all bugs, comments, and changes to sudo-bugs@courtesan.com. +.SH "DISCLAIMER" +.IX Header "DISCLAIMER" +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. +.PP +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., +675 Mass Ave, Cambridge, MA 02139, USA. +.SH "CAVEATS" +.IX Header "CAVEATS" +Due to the syntax of the \fIsudoers\fR file, there is no way +for \fBvisudo\fR to tell the difference between a mistyped +{Host,User,Cmnd}_Alias and a user or host name. +.PP +There is no easy way to prevent a user from gaining a root shell if +the editor used by \fBvisudo\fR allows shell escapes. +.SH "BUGS" +.IX Header "BUGS" +The \fI\-V\fR flag gives the version of the \fIsudo\fR package rather than +the individual \fBvisudo\fR program. +.SH "SEE ALSO" +.IX Header "SEE ALSO" +\fIsudo\fR\|(8), \fIvipw\fR\|(8). + +.rn }` '' diff --git a/gnu/usr.bin/sudo/visudo/visudo.c b/gnu/usr.bin/sudo/visudo/visudo.c new file mode 100644 index 00000000000..85099e75715 --- /dev/null +++ b/gnu/usr.bin/sudo/visudo/visudo.c @@ -0,0 +1,512 @@ +/* + * CU sudo version 1.5.2 + * + * 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 1, 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., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Please send bugs, changes, problems to sudo-bugs@courtesan.com + * + ******************************************************************* + * + * visudo.c -- locks the sudoers file for safe editing and check + * for parse errors. + * + * Todd C. Miller (millert@colorado.edu) Sat Mar 25 21:50:36 MST 1995 + */ + +#ifndef lint +static char rcsid[] = "$Id: visudo.c,v 1.1 1996/10/14 05:14:58 millert Exp $"; +#endif /* lint */ + +#include "config.h" + +#include <stdio.h> +#ifdef STDC_HEADERS +#include <stdlib.h> +#endif /* STDC_HEADERS */ +#ifdef HAVE_UNISTD_H +#include <unistd.h> +#endif /* HAVE_UNISTD_H */ +#ifdef HAVE_STRING_H +#include <string.h> +#endif /* HAVE_STRING_H */ +#ifdef HAVE_STRINGS_H +#include <strings.h> +#endif /* HAVE_STRINGS_H */ +#if defined(HAVE_MALLOC_H) && !defined(STDC_HEADERS) +#include <malloc.h> +#endif /* HAVE_MALLOC_H && !STDC_HEADERS */ +#include <ctype.h> +#include <pwd.h> +#include <signal.h> +#include <errno.h> +#include <fcntl.h> +#include <sys/types.h> +#include <sys/param.h> +#include <sys/stat.h> +#include <sys/file.h> +#include <netinet/in.h> + +#include "sudo.h" +#include <options.h> +#include "version.h" + +#ifndef STDC_HEADERS +#ifndef __GNUC__ /* gcc has its own malloc */ +extern char *malloc __P((size_t)); +#endif /* __GNUC__ */ +extern char *getenv __P((const char *)); +extern int stat __P((const char *, struct stat *)); +#endif /* !STDC_HEADERS */ + +#if defined(POSIX_SIGNALS) && !defined(SA_RESETHAND) +#define SA_RESETHAND 0 +#endif /* POSIX_SIGNALS && !SA_RESETHAND */ + +/* + * Function prototypes + */ +static void usage __P((void)); +static char whatnow __P((void)); +static void whatnow_help __P((void)); +static RETSIGTYPE Exit __P((int)); +static void setup_signals __P((void)); +int command_matches __P((char *, char *, char *, char *)); +int addr_matches __P((char *)); +int netgr_matches __P((char *, char *, char *)); +int usergr_matches __P((char *, char *)); +void init_parser __P((void)); + + +/* + * External globals + */ +extern FILE *yyin, *yyout; +extern int errorlineno, sudolineno; + + +/* + * Globals + */ +char **Argv; +char **NewArgv = NULL; +int NewArgc = 0; +char *sudoers = _PATH_SUDO_SUDOERS; +char *stmp = _PATH_SUDO_STMP; +int parse_error = FALSE; +char *runas_user = "root"; + +/* + * For the parsing routines + */ +char host[] = ""; +char *shost = ""; +char *cmnd = ""; +char *cmnd_args = NULL; +struct passwd *user_pw_ent; + + +/******************************************************************** + * + * main() + * + * where it all begins... + */ + +int main(argc, argv) + int argc; + char **argv; +{ + char buf[BUFSIZ]; /* buffer used for copying files */ + char * Editor = EDITOR; /* editor to use (default is EDITOR */ + int sudoers_fd; /* sudoers file descriptor */ + int stmp_fd; /* stmp file descriptor */ + int n; /* length parameter */ + + (void) setbuf(stderr, (char *)NULL); /* unbuffered stderr */ + + /* + * Parse command line options + */ + Argv = argv; + + /* + * If passesd -V then print version, else print usage + * if any other option... + */ + if (argc == 2) + if (!strcmp(Argv[1], "-V")) { + (void) printf("visudo version %s\n", version); + exit(0); + } else { + usage(); + } + else if (argc != 1) + usage(); + + /* user_pw_ent needs to point to something... */ + user_pw_ent = getpwuid(getuid()); + +#ifdef ENV_EDITOR + /* + * If we are allowing EDITOR and VISUAL envariables set Editor + * base on whichever exists... + */ + if (!(Editor = getenv("EDITOR"))) + if (!(Editor = getenv("VISUAL"))) + Editor = EDITOR; +#endif /* ENV_EDITOR */ + + /* + * Copy sudoers file to stmp + */ + stmp_fd = open(stmp, O_WRONLY | O_CREAT | O_EXCL, 0600); + if (stmp_fd < 0) { + if (errno == EEXIST) { + (void) fprintf(stderr, "%s: sudoers file busy, try again later.\n", + Argv[0]); + exit(1); + } + (void) fprintf(stderr, "%s: ", Argv[0]); + perror(stmp); + Exit(1); + } + + /* install signal handler to clean up stmp */ + setup_signals(); + + sudoers_fd = open(sudoers, O_RDONLY); + if (sudoers_fd < 0) { + (void) fprintf(stderr, "%s: ", Argv[0]); + perror(sudoers); + Exit(1); + } + + /* + * Copy the data + */ + while ((n = read(sudoers_fd, buf, sizeof(buf))) > 0) + if (write(stmp_fd, buf, n) != n) { + (void) fprintf(stderr, "%s: Write failed: ", Argv[0]); + perror(""); + Exit(1); + } + + (void) close(sudoers_fd); + (void) close(stmp_fd); + + /* + * Edit the temp file and parse it (for sanity checking) + */ + do { + /* + * Build up a buffer to execute + */ + if (parse_error == TRUE) + (void) sprintf(buf, "%s +%d %s", Editor, errorlineno, stmp); + else + (void) sprintf(buf, "%s %s", Editor, stmp); + + /* do the edit -- some SYSV editors return 256 instead of 0 */ + n = system(buf); + if (n == 0 || n == 256) { + struct stat statbuf; /* for sanity checking */ + + /* make sure stmp exists */ + if (stat(stmp, &statbuf) < 0) { + (void) fprintf(stderr, + "%s: Can't stat temporary file (%s), %s unchanged.\n", + Argv[0], stmp, sudoers); + Exit(1); + } + + /* check for zero length file */ + if (statbuf.st_size == 0) { + (void) fprintf(stderr, + "%s: Zero length temporary file (%s), %s unchanged.\n", + Argv[0], stmp, sudoers); + Exit(1); + } + + /* + * passed sanity checks so reopen stmp file and check + * for parse errors. + */ + yyout = stdout; + if (parse_error) + yyin = freopen(stmp, "r", yyin); + else + yyin = fopen(stmp, "r"); + if (yyin == NULL) { + (void) fprintf(stderr, + "%s: Can't re-open temporary file (%s), %s unchanged.\n", + Argv[0], stmp, sudoers); + Exit(1); + } + + /* clean slate for each parse */ + init_parser(); + + /* parse the sudoers file */ + if (yyparse()) { + (void) fprintf(stderr, + "%s: Failed to parse temporary file (%s), %s unchanged.\n", + Argv[0], stmp, sudoers); + Exit(1); + } + } else { + (void) fprintf(stderr, "%s: Editor (%s) failed, %s unchanged.\n", + Argv[0], Editor, sudoers); + Exit(1); + } + + /* + * Prompt the user for what to do now + */ + if (parse_error == TRUE) { + switch (whatnow()) { + case 'q' : parse_error = FALSE; /* ignore parse error */ + break; + case 'x' : Exit(0); + break; + } + } + } while (parse_error == TRUE); + + /* + * Change mode and ownership of temp file so when + * we move it to sudoers things are kosher. + */ + if (chown(stmp, SUDOERS_UID, SUDOERS_GID)) { + (void) fprintf(stderr, + "%s: Unable to set (uid, gid) of %s to (%d, %d): ", + Argv[0], stmp, SUDOERS_UID, SUDOERS_GID); + perror(""); + Exit(1); + } + if (chmod(stmp, SUDOERS_MODE)) { + (void) fprintf(stderr, + "%s: Unable to change mode of %s to %o: ", + Argv[0], stmp, SUDOERS_MODE); + perror(""); + Exit(1); + } + + /* + * Now that we have a sane stmp file (parse ok) it needs to be + * rename(2)'d to sudoers. If the rename(2) fails we try using + * mv(1) in case stmp and sudoers are on different filesystems. + */ + if (rename(stmp, sudoers)) + if (errno == EXDEV) { + char *tmpbuf; + + (void) fprintf(stderr, + "%s: %s and %s not on the same filesystem, using mv to rename.\n", + Argv[0], stmp, sudoers); + + /* Allocate just enough space for tmpbuf */ + n = sizeof(char) * (strlen(_PATH_MV) + strlen(stmp) + + strlen(sudoers) + 4); + if ((tmpbuf = (char *) malloc(n)) == NULL) { + (void) fprintf(stderr, + "%s: Cannot alocate memory, %s unchanged: ", + Argv[0], sudoers); + perror(""); + Exit(1); + } + + /* Build up command and execute it */ + (void) sprintf(tmpbuf, "%s %s %s", _PATH_MV, stmp, sudoers); + if (system(tmpbuf)) { + (void) fprintf(stderr, + "%s: Command failed: '%s', %s unchanged.\n", + Argv[0], tmpbuf, sudoers); + Exit(1); + } + (void) free(tmpbuf); + } else { + (void) fprintf(stderr, "%s: Error renaming %s, %s unchanged: ", + Argv[0], stmp, sudoers); + perror(""); + Exit(1); + } + + return(0); +} + + +/******************************************************************** + * + * dummy *_matches routines + * + * These exist to allow us to use the same parser as sudo(8). + */ + +int command_matches(cmnd, user_args, path, sudoers_args) + char *cmnd; + char *user_args; + char *path; + char *sudoers_args; +{ + return(TRUE); +} + + +int addr_matches(n) + char *n; +{ + return(TRUE); +} + +int usergr_matches(g, u) + char *g, *u; +{ + return(TRUE); +} + + +int netgr_matches(n, h, u) + char *n, *h, *u; +{ + return(TRUE); +} + + +/******************************************************************** + * + * usage() + * + * Prints a help message and exits w/ exit value of 1. + */ + +static void usage() +{ + (void) fprintf(stderr, "usage: %s [-V]\n", Argv[0]); + Exit(1); +} + + +/******************************************************************** + * + * Exit() + * + * Unlinks the sudoers temp file (if it exists) and exits. + * Used in place of a normal exit() and as a signal handler. + */ + +static RETSIGTYPE Exit(sig) + int sig; +{ + (void) unlink(stmp); + exit(sig); +} + + +/******************************************************************** + * + * whatnow() + * + * Assuming a parse error occurred, prompt the user for what they want + * to do now. Returns first letter of their choice (always lowercase). + */ + +static char whatnow() +{ + char choice; + int ok; + + do { + ok = FALSE; + (void) printf("What now? "); + if ((choice = fgetc(stdin)) != '\n') + while (fgetc(stdin) != '\n') + ; + + /* safely force to lower case */ + if (isupper(choice)) + choice = tolower(choice); + + if (choice == 'e' || choice == 'x' || choice == 'q') + ok = TRUE; + + /* help message if they gavce us garbage */ + if (!ok) + whatnow_help(); + + } while (!ok); + + return(choice); +} + + +/******************************************************************** + * + * whatnow_help() + * + * Print out a help message for whatnow(). + */ + +static void whatnow_help() +{ + (void) printf("Options are:\n"); + (void) printf(" (e)dit sudoers file again\n"); + (void) printf(" e(x)it without saving changes to sudoers file\n"); + (void) printf(" (q)uit and save changes to sudoers file (DANGER!)\n\n"); +} + + +/******************************************************************** + * + * setup_signals() + * + * Install signal handlers for visudo. + */ + +static void setup_signals() +{ +#ifdef POSIX_SIGNALS + struct sigaction action; /* posix signal structure */ +#endif /* POSIX_SIGNALS */ + + /* + * Setup signal handlers + */ +#ifdef POSIX_SIGNALS + (void) memset((VOID *)&action, 0, sizeof(action)); + action.sa_handler = Exit; + action.sa_flags = SA_RESETHAND; + (void) sigaction(SIGILL, &action, NULL); + (void) sigaction(SIGTRAP, &action, NULL); + (void) sigaction(SIGBUS, &action, NULL); + (void) sigaction(SIGSEGV, &action, NULL); + (void) sigaction(SIGTERM, &action, NULL); + + action.sa_handler = SIG_IGN; + action.sa_flags = 0; + (void) sigaction(SIGHUP, &action, NULL); + (void) sigaction(SIGINT, &action, NULL); + (void) sigaction(SIGQUIT, &action, NULL); +#else + (void) signal(SIGILL, Exit); + (void) signal(SIGTRAP, Exit); + (void) signal(SIGBUS, Exit); + (void) signal(SIGSEGV, Exit); + (void) signal(SIGTERM, Exit); + + (void) signal(SIGHUP, SIG_IGN); + (void) signal(SIGINT, SIG_IGN); + (void) signal(SIGQUIT, SIG_IGN); +#endif /* POSIX_SIGNALS */ +} |