sources-request@mirror.TMC.COM (12/06/86)
Submitted by: ihnp4!yetti!lethe!dave Mod.sources: Volume 7, Issue 80 Archive-name: append Here is a shar archive of the program "append", which fixes a small security hole for large unix sites. The files "append.c" and "append.1" are necessary, the rest (secur.r, append.web and append.r) are merely of interest: 1) Source for append in C (the output of TANGLE, edited for readability by humans. See 5, below). 2) The man-page for append(1) in nroff input format. 3) An essay on security -vs- freedom to donate programs on a multi-user site. 4) The WEB input form of append (which was written in C using Knuth's WEB "literate programming" system) 5) The nroff input form of append (after processing with WEAVE), suitable for nroff -ms for a programming logic manual. --dave -----CUT HERE----- #!/bin/sh echo "x - append.c" sed "s/^X//" > append.c <<'!-E-o-F' X/* 4.0: */ X#line 48 append.web X X/* 4.4: */ X#line 253 append.web X X/* X * append -- a command to append a file to a directory to which X * one does not have write permission, using setgrp & ln. X * Placing append in a directory simulates the "sa a *.*" X * access control command of Multics. See also append.web. X */ X X/* :4.4 */ X X#line 261 append.web X X X/* 4.2: */ X#line 187 append.web X X#include <stdio.h> X#include <errno.h> X#include <sys/types.h> X#include <sys/stat.h> X X/* :4.2 */ X X#line 193 append.web X X X/* 4.1: */ X#line 104 append.web X X#define ERR (-1) X X/* :4.1 */ X X#line 107 append.web X/* 4.2: */ X#line 197 append.web X X#define DIRECTORY 0040000 X#define SPECIAL (0020000 | 0060000) X X/* :4.2 */ X X#line 201 append.web X X X/* 4.1: */ X#line 61 append.web X X void Xmain(argc,argv) int argc; char *argv[]; { X /* 4.2: */ X#line 193 append.web X X struct stat s; /* stat buffer */ X int m; /* file access mode */ X X /* :4.2 */ X X#line 197 append.web X /* 4.3: */ X#line 246 append.web X X extern int errno; X extern char *sys_errlist[]; X int rc; X X /* :4.3 */ X X#line 251 append.web X X X char *progName, *fromName, *toName, X *segmentPart(/* char * */); X X progName = argv[0]; X if (argc < 2) { X /* no parms, must be a request for information */ X /* 4.4: */ X#line 261 append.web X X fprintf(stderr,"%s -- add (via link) a file to this directory, %s\n", X progName, "even if you lack permission."); X fprintf(stderr,"Usage: %s filename [newname]\n",progName); X X /* :4.4 */ X X#line 266 append.web X X X exit(0); X } X else if (argc == 2) { X /* one parm, make names the same */ X fromName = argv[1]; X toName = segmentPart(argv[1]); X } X else if (argc == 3) { X /* two parms, make second one the new name */ X fromName = argv[1]; X if (strcmp(argv[2],".")==0) { X /* the directory */ X toName = segmentPart(fromName); X } X else { X toName = segmentPart(argv[2]); X } X } X/* :4.1 */ X X#line 90 append.web X/* 4.1: */ X#line 96 append.web X X X /* 4.2: */ X#line 139 append.web X X if (stat(progName,&s) == ERR || s.st_mode & (DIRECTORY|SPECIAL) X || (s.st_uid != geteuid() && s.st_gid != getegid())) { X /* someone's trying to trick me by putting append in his path */ X fprintf(stderr,"%s: Can't append to current directory, %s\n", X progName, "\"cd\" to target directory first"); X exit(1); X } X X X if (stat(fromName,&s) == ERR) { X switch (errno) { X case ENOTDIR: X fprintf(stderr,"%s: Can't access %s, %s.\n", progName, X fromName, "part of the path is a non-directory"); X break; X case ENOENT: X fprintf(stderr,"%s: File %s doesn't exist.\n", X progName,fromName); X break; X case EACCES: X fprintf(stderr,"%s: Can't search a directory in %s.\n", X progName,fromName); X break; X default: X fprintf(stderr,"%s: Can't link, error is \"%s\".\n", X progName,sys_errlist[errno]); X } X exit(1); X } X else if ((m=s.st_mode) & SPECIAL) { X fprintf(stderr,"%s: Can't append a special file.\n",progName); X exit(1); X } X else if (m & DIRECTORY) { X fprintf(stderr,"%s: Can't append a directory.\n",progName); X exit(1); X } X X if (stat(toName,&s) != ERR) { X fprintf(stderr,"%s: Can't replace an existing file\n", X progName); X exit(1); X } X /* :4.2 */ X X#line 183 append.web X X X rc = link(fromName,toName); X /* 4.3: */ X#line 207 append.web X X if (rc == ERR) { X switch (errno) { X case EACCES: X fprintf(stderr,"%s: Cannot write to this directory (%s).\n", X progName, "Can't happen, send mail to the owner"); X exit(3); X case EXDEV: X fprintf(stderr,"%s: Can't do a cross-device link.\n", X progName); X exit(1); X case EROFS: X fprintf(stderr,"%s: Can't link to a r/o file system.\n", X progName); X exit(1); X case EMLINK: X fprintf(stderr,"%s: Can't link, too many already exist.\n", X progName); X exit(1); X case ENOSPC: X fprintf(stderr,"%s: Can't link, directory full.\n", X progName); X exit(1); X case ENOTDIR: X case ENOENT: X case EEXIST: X case EPERM: X fprintf(stderr,"%s: Can't link, impossible error \"%s\".\n", X progName,sys_errlist[errno]); X exit(3); X default: X fprintf(stderr,"%s: Can't link, error is \"%s\".\n", X progName,sys_errlist[errno]); X exit(2); X } X } X /* :4.3 */ X X#line 243 append.web X X X exit(0); X} X X/* :4.1 */ X X#line 104 append.web X X X/* 5.0: */ X#line 272 append.web X X char * XsegmentPart(s) char *s; { X char *p, *strrchr(/* char *, char */); X X if (s && (p=strrchr(s,'/'))) { X return ++p; X } X return s; X} X X/* :5.0 */ X X#line 282 append.web X X X X/* :4.0 */ X X#line 55 append.web X !-E-o-F echo "x - append.1" sed "s/^X//" > append.1 <<'!-E-o-F' X.TH APPEND 1 Local X.SH NAME Xappend \- append a file to the current directory X.SH SYNOPSIS X.B append Xfilename [newname] X.SH DESCRIPTION X.PP XAppend is a command to allow another person to put a file (actually Xa link) into a particular directory. This simulates the "append only" Xaccess permission allowed by Multics but not Unix. X.PP XThe mechanism is quite simple: the program is setuid to the owner of Xthe directory, and when called checks the file for plausibility Xand links it into the current directory. X.PP XThe use of a link allows the author to modify (ie, maintain) the program Xwithout requiring the superuser to delete and reinstall the new version Xin the target directory. X.SH INSTALLATION X.PP XTo install append, place a copy into the directory you wish to Xhave accessable to others, chown it to yourself, and Xset it setuid and executable, but not writable. XThe chown command is: X.br X chown your_name append X.br XThe chmod command for this is: X.nf X chmod u+s,g-w,o-w,a+x append -- can be used by anyone X or X chmod u+s,g-w,o-w,g+x append -- can be used by group only X.fi X.SH "CHECKING THE ADVISABILITY OF APPENDING" X.PP XIt is inadvisable to append many kinds of file to a directory, Xsuch as directorys and special files, and of course the file Xshould be present and either readable or executable. XAppend checks for accessability and plausability. X.PP XPlease note that it is quite reasonable to append a setuid Xor setgid program to a directory. In fact, this is the proper Xway to place a "gate" to certain private or sensitive Xinformation in a generally accesable place. It is not Xreasonable to contrive to chown and re-setuid a program Xto belong to root and append it to a directory, but this is dealt Xwith by chown and chmod directly, and does not affect append. X.PP XThis does not mean that giving anyone append permissions on /bin is Xa good idea: it is not hard to write a program which contains a Xtrapdoor to catch the superuser, and append on /bin or /usr/bin Xwould make it easy to put it in his way. X.PP XIn general, one places append permissions on directories like X/usr/local/bin (which superuser doesn't normally search for commands) Xand transfer directories. Uucppublic would have been an excellent Xexample if the uucp author known about this technique... X.PP XOne can append a FIFO to a directory even though it Xis a type of "special" file, since this does not constitute an X(obvious) security problem. X.SH FILES XNone. X.SH "SEE ALSO" Xcp(1), section on "ln", append.web for detailed discussion of the Xalgorithm. X.SH DIAGNOSTICS XSelf-explanatory, one hopes. X.SH BUGS X.PP XIt is possible, as mentioned above, to install a trapdoor program Xusing append in /bin, /usr/bin or /etc: do not grant append permissions Xto these directories. X.PP XThere is no companion "unappend" program, since that would be a Xsecurity bug of the first order. X.PP XYou must have cd'd to the target directory to use append. X X !-E-o-F echo "x - secur.r" sed "s/^X//" > secur.r <<'!-E-o-F' X.TL XA Short Essay on Unix Security X.AU XDavid R. Brown X(lethe!dave, watmath!watbun!drbrown) X.AB X.PP XThis note discusses a small disfeature in Unix Xin light of Xthe Multics experience, and shows how to block it using a Xclassical Unix technique. X.AE X X.SH XIntroduction X.PP XOn Unix systems where there are more than a few developers, an interesting Xproblem often occurs. To install a command in the user library, Xone has to be root. To be root gives one the power to do Xserious damage accidentally, and is therefore inadvisable. XTo have to interrupt the systems administrator to have him (her) Xinstalling and deinstalling programs can make one unpopular. X.PP XTherefore numerous sites do not prohibit writing to /usr/bin or Xsome other site-specific bin directory. XAs you might expect, this is X.B Xnot Xa good idea. X.PP XConversely, numerous sites do not allow developers to add to any Xsharable library at all, thus making the developers little islands Xof private (indeed, peculiar) tools. X.PP XOthers officially refuse to allow access to shared libraries, but Xunofficially allow the root password to become widely known. X X.SH XConsequences X.PP XThis last technique, just pretending to prohibit access, Xis by far the most common, since the administrator can Xclaim to be secure and the developers can still share the fruits Xof their efforts. X.PP XIt is also a recipe for disaster. X.PP XTry to imagine the expression on a manager's face when he discovers Xthat the reorganization he just did resulted Xin the loss of about six months of his boss's work... XImagine the expression when he can't figure out how to do a restore Xwithout asking operations. XFinally, imagine what it make him look like to the boss who was Xpromised that his data was secure from his subordinates. X.PP XI got to see this happen at more than one site in more than one company. XIt is humorous only to the onlooker. It is a career-eater to the Xparticipants. X X.PP XRefusing to allow any access to the bin directories results in a maze Xof private, peculiar environments, one per programmer. If the site Xis consistent in refusing access to other shared directories X(xxx/include, xxx/lib), then when programmers try to cooperate on Xa project, they find themselves artificially prevented from sharing Xsource and libraries. X.PP XThis tends to make a development team into a collection of independent Xindividuals, if not a collection of prima donnas. X X.PP XLeaving write permission on /usr/bin or the like, while guarding Xthe superuser password, leaves a different security hole open, the Xtraditional "trapdoor" hole. X.PP XA person with access to a bin directory writes a utility program which, Xwhile doing its normal task, checks to see if it is being executed by root, Xand if so creates a setuid-root shell for the author. XFrom that day onward, the author of the trapdoor program is root, even Xif the root password is changed and kept secret. X X.SH XOther Misfeatures X.PP XThere are some other problems, on first glance unrelated, that Xarise out of the same deficiency. X.PP XFirstly, one cannot place "gates" to sharable information in Xa public place unless the place is writable by all. This makes Xit difficult to allow access to a file which you have saved Xaway down an unsearchable filetree, unless you put links to it Xin someone's home directory. The person looking for the information Xcan find it, but has to remember where to look. X X.PP XAlso, one cannot easily have an accessible but private place for transfer Xprograms to put things: instead you get the UUCP public directory, Xand the security (and administration!) problem that creates. X X.SH XAttempted Cures X.PP XThe most common attempt to cure the above lies in creating "project" Xor "public" accounts, into which project-specific and public information Xis placed. X.PP XThis really doesn't change a thing. The bin directories of Xthe public account can be trapdoored by anyone, the data in the account Xcan be changed or lost by a careless mistake, and access to anyone with Xthe password is possible, even to private or confidential information. X.PP XIn other words, the password to superuser is unnecessary to a cracker. XThe password to the public account becomes sufficient for building Xtrapdoors. X.PP XThere are other kludges to allow controlled sharing, but all are as bad or Xworse than public accounts. That does not mean that there is no real solution, Xmerely that it is not obvious. X X.SH XMultics X.PP XOnce upon a time, an operating system was written by Project MAC of M.I.T, XBell Laboratories and the General Electric Company. This was Multics, the Xdirect predecessor of Unix. X.PP XOn Multics, security was a design consideration. Sometimes too much of Xa design consideration according to the Hackers of the MIT AI Lab. XNevertheless, it was written with controled sharing in mind. X.PP XOne of the design features which security considerations affected was the Xpermissions applied to directories. In Multics, one didn't have read, Xwrite and execute permissions on directories, one had search, modify Xand append. X.PP XThis made the directories different from ordinary files and therefore Xrequired more special-case code (to the detriment of elegance), but it Xwas a useful distinction. X.PP XSearch was permission to read a directory, modify permission to write Xit. Append was permission to add a file to the directory. Therefore Xif on had "sa", one could see what was in the directory and add files Xor links, but one could not modify what was already there. X X.PP XThis made it easy to share things: one said "SetAccess sa *.MyProject" Xto allow anyone on your project (group) to search or append to a directory. X.PP XPrograms used it to allow anyone to add a "letter" to a "forum" (the Multics Xversion of news) without giving them the ability to modify or remove Xother person's letters. X.PP XPersons working on new commands would be granted append permission to the X"EXperimentalLibrary" to put programs out to beta-test (EXL would amount to Xsomething like /usr/local/experimental, or perhaps /usr/local/bin). X.PP XPersons alpha-testing programs would grant append permissions to their Xpersonal libraries ($HOME/bin) to let others add the new commands at Xtheir leisure. X X.SH XUnix X.PP XUnix does not have "sma" permissions on directories, and does not Xneed them. Instead Unix has a mechanism to allow a program to Xgrant permissions temporarily to its users, the "setuid" bit. X X.PP XSetuid allows controlled access to things which belong to individuals Xto others. Since a program may make quite complex decisions, it actually Xallows a finer degree of control than the Multics access control lists. X.PP XSetgid (set group id) allows similar access to things which belong to a central Xutility to selected groups of individuals. For purely historical reasons Xit is not often used. X X.PP XWith these capabilities, it is possible on Unix to write a program which Xallows the kind of controlled sharing for which Multics was designed. X X.SH XAppend Program X.PP XThe program in question is called "append", and allows either anyone in Xthe same group, or optionally anyone at all Xto append files to the current directory (only). X.PP XIt does this very simply: It checks that the file to be added is not a Xdirectory or special file, and links it into the current directory. XIt is able to do so because it is setuid (or setgid) to the owner or Xgroup of the directory. X.PP XVoila! Multics access control for Unix. X X.SH XImprovements X.PP XBecause the directory is not writable by others, append could optionally Xcheck for a ".acl" file and allow appending only by people listed in it. X.PP XThis would simulate the full set of access control directives provided Xon Unix's grampa. X X.SH XCaveats X.PP XOne should set append on /usr/local/bin, not on any directory in the Xcommand search-path of super-user, or you will have created one of Xthe security problems append is supposed to prevent. X X.nf X -- Dave (Unix hack on a 'bun) Brown X yetti!lethe!dave X (formerly DRBrown@HI-Multics.ARPA) X X !-E-o-F echo "x - append.web" sed "s/^X//" > append.web <<'!-E-o-F' X.TL XAppend, XA Program to Simulate "append" Permissions on Unix. X.AU XDave Brown X(yetti!lethe!dave, watmath!watbun!drbrown) X.AB XAppend.web is the (complete) implementation of a program Xwhich closes a small security hole in Unix, by allowing Xselected persons to add to (but not modify) the contents Xof a directory. X.PP XWith append permissions, one can add or update user-supported Xsoftware without requiring superuser priveleges, or can Xplace gateways to otherwise inaccessible programs or data Xin publicly accessible places. X.AE X@*Append@> X.PP XAppend is a command to allow another person to put a file (actually Xa link) into a particular directory. This simulates the "append only" Xaccess permission allowed by Multics but not Unix. X.PP XThe mechanism is quite simple: the program is setuid to the owner of Xthe directory, and when called simply checks the file for plausability Xand links it into the current directory. X X@*Installation@> X.PP XTo install append, place a copy into the directory you wish to Xhave accessible to others, set it setuid and executable, but not Xwritable. The chmod command for this is: X.nf X chmod u+s,g-w,o-w,a+x append -- accessible to anyone X or X chmod u+s,g-w,o-w,o-x,g+x append -- accessible to group X.fi X.PP XIf you take a copy of mine, you will also have to "chown" it to yourself X.I Xbefore Xyou chmod it setuid. X X@*Implementation@> X.PP XThe program consists of a main routine and some ancillaries, thusly: X X@<Append@>= X<Header> X<Includes> X<Globals> X<Main> X<Utilities> X X@ Main Program@> X.PP XAppend consists of three basic operations. First it checks to see Xif you want usage information, then it checks that the file is acceptable Xand finally it links it in. X X@<Main@>= X void Xmain(argc,argv) int argc; char *argv[]; { X <Declarations> X char *progName, *fromName, *toName, X *segmentPart(/* char * */); X X progName = argv[0]; X if (argc < 2) { X /* no parms, must be a request for information */ X <Provide Usage> X exit(0); X } X else if (argc == 2) { X /* one parm, make names the same */ X fromName = argv[1]; X toName = segmentPart(argv[1]); X } X else if (argc == 3) { X /* two parms, make second one the new name */ X fromName = argv[1]; X if (strcmp(argv[2],".")==0) { X /* the directory */ X toName = segmentPart(fromName); X } X else { X toName = segmentPart(argv[2]); X } X } X@t X.PP XNote, please that toName is always the segment (filename) part of Xthe desired location. This prevents one from saying "append x ../x" Xand making x appear in some directory you don't want append permissions Xapplied to. X@p X X <Validity Check> X rc = link(fromName,toName); X <Completion Check> X exit(0); X} X X@<Globals@>= X#define ERR (-1) X X@ Checking the Advisability of Appending@> X.PP XIt is inadvisable to append many kinds of file to a directory, Xsuch as directorys and special files, and of course the file Xshould be present and either readable or executable. XThis block checks for accessibility and plausibility. X X.PP XPlease note that it is reasonable to append a setuid Xor setgid program to a directory. In fact, this is the proper Xway to place a "gate" to certain private or sensitive Xinformation in a generally accessible place. It is not Xreasonable to contrive to chown and re-setuid a program Xto belong to root and append it to a directory, but this is dealt Xwith by chown and chmod directly, and does not affect append. X.PP XThis does not mean that giving anyone append permissions on /bin is Xa good idea: it is not hard to write a program which contains a Xtrapdoor to catch the superuser, and append on /bin or /usr/bin Xwould make it easy to put it in his way. X.PP XIn general, one places append permissions on directories like X/usr/local/bin (which superuser doesn't normally search for commands), Xman-page directories and transfer directories. XUucppublic would have been an excellent Xexample if the uucp author had thought of this technique... X X.PP XOne can append a FIFO to a directory even though it Xis a type of "special" file, since this does not constitute an X(obvious) security problem. X X@<Validity Check@>= Xif (stat(progName,&s) == ERR || s.st_mode & (DIRECTORY|SPECIAL) X || (s.st_uid != geteuid() && s.st_gid != getegid())) { X /* someone's trying to trick me by putting append in his path */ X fprintf(stderr,"%s: Can't append to current directory, %s\n", X progName, "\"cd\" to target directory first"); X exit(1); X} X X Xif (stat(fromName,&s) == ERR) { X switch (errno) { X case ENOTDIR: X fprintf(stderr,"%s: Can't access %s, %s.\n", progName, X fromName, "part of the path is a non-directory"); X break; X case ENOENT: X fprintf(stderr,"%s: File %s doesn't exist.\n", X progName,fromName); X break; X case EACCES: X fprintf(stderr,"%s: Can't search a directory in %s.\n", X progName,fromName); X break; X default: X fprintf(stderr,"%s: Can't link, error is \"%s\".\n", X progName,sys_errlist[errno]); X } X exit(1); X} Xelse if ((m=s.st_mode) & SPECIAL) { X fprintf(stderr,"%s: Can't append a special file.\n",progName); X exit(1); X} Xelse if (m & DIRECTORY) { X fprintf(stderr,"%s: Can't append a directory.\n",progName); X exit(1); X} X Xif (stat(toName,&s) != ERR) { X fprintf(stderr,"%s: Can't replace an existing file\n", X progName); X exit(1); X} X@t X.PP XThe block depends upon the following declarations and includes: X X@<Includes@>= X#include <stdio.h> X#include <errno.h> X#include <sys/types.h> X#include <sys/stat.h> X X@<Declarations@>= Xstruct stat s; /* stat buffer */ Xint m; /* file access mode */ X X@<Globals@>= X#define DIRECTORY 0040000 X#define SPECIAL (0020000 | 0060000) X X@ Checking the Results of Appending@> X.PP XThe link can fail for several reasons, notably because Unix Xcan't do cross-device links, link to a read-only file system Xand so forth. X X@<Completion Check@>= Xif (rc == ERR) { X switch (errno) { X case EACCES: X fprintf(stderr,"%s: Cannot write to this directory (%s).\n", X progName, "Can't happen, send mail to the owner"); X exit(3); X case EXDEV: X fprintf(stderr,"%s: Can't do a cross-device link.\n", X progName); X exit(1); X case EROFS: X fprintf(stderr,"%s: Can't link to a r/o file system.\n", X progName); X exit(1); X case EMLINK: X fprintf(stderr,"%s: Can't link, too many already exist.\n", X progName); X exit(1); X case ENOSPC: X fprintf(stderr,"%s: Can't link, directory full.\n", X progName); X exit(1); X case ENOTDIR: X case ENOENT: X case EEXIST: X case EPERM: X fprintf(stderr,"%s: Can't link, impossible error \"%s\".\n", X progName,sys_errlist[errno]); X exit(3); X default: X fprintf(stderr,"%s: Can't link, error is \"%s\".\n", X progName,sys_errlist[errno]); X exit(2); X } X} X@t X.PP XThese depend in turn on the following: X@<Declarations@>= Xextern int errno; Xextern char *sys_errlist[]; Xint rc; X X@ User Header@> X X@<Header@>= X/* X * append -- a command to append a file to a directory to which X * one does not have write permission, using setgrp & ln. X * Placing append in a directory simulates the "sa a *.*" X * access control command of Multics. See also append.web. X */ X X@<Provide Usage@>= Xfprintf(stderr,"%s -- add (via link) a file to this directory, %s\n", X progName, "even if you lack permission."); Xfprintf(stderr,"Usage: %s filename [newname]\n",progName); X X@*Utility Routines@> X.PP XThis program has a single utility routine, "segmentPart", which Xextracts the segment/filename part of a pathname string, by returning Xeverything right of the last "/". X X@<Utilities@>= X char * XsegmentPart(s) char *s; { X char *p, *strrchr(/* char *, char */); X X if (s && (p=strrchr(s,'/'))) { X return ++p; X } X return s; X} X !-E-o-F echo "x - append.r" sed "s/^X//" > append.r <<'!-E-o-F' X.DA X.TL XAppend, XA Program to Simulate "append" Permissions on Unix. X.AU XDave Brown X(yetti!lethe!dave, watmath!watbun!drbrown) X.AB XAppend.web is the (complete) implementation of a program Xwhich closes a small security hole in Unix, by allowing Xselected persons to add to (but not modify) the contents Xof a directory. X.PP XWith append permissions, one can add or update user-supported Xsoftware without requiring superuser priveleges, or can Xplace gateways to otherwise inaccessible programs or data Xin publicly accessible places. X.AE X X.fi X.ec X.NH XAppend X.br X.PP XAppend is a command to allow another person to put a file (actually Xa link) into a particular directory. This simulates the "append only" Xaccess permission allowed by Multics but not Unix. X.PP XThe mechanism is quite simple: the program is setuid to the owner of Xthe directory, and when called simply checks the file for plausability Xand links it into the current directory. X X X.fi X.ec X.NH XInstallation X.br X.PP XTo install append, place a copy into the directory you wish to Xhave accessible to others, set it setuid and executable, but not Xwritable. The chmod command for this is: X.nf X chmod u+s,g-w,o-w,a+x append -- accessible to anyone X or X chmod u+s,g-w,o-w,o-x,g+x append -- accessible to group X.fi X.PP XIf you take a copy of mine, you will also have to "chown" it to yourself X.I Xbefore Xyou chmod it setuid. X X X.fi X.ec X.NH XImplementation X.br X.PP XThe program consists of a main routine and some ancillaries, thusly: X X.B X.br X<Append>= X.R X.eo X.nf X<Header> X<Includes> X<Globals> X<Main> X<Utilities> X X X.fi X.ec X.NH 2 XMain Program X.br X.PP XAppend consists of three basic operations. First it checks to see Xif you want usage information, then it checks that the file is acceptable Xand finally it links it in. X X.B X.br X<Main>= X.R X.eo X.nf X void Xmain(argc,argv) int argc; char *argv[]; { X <Declarations> X char *progName, *fromName, *toName, X *segmentPart(/* char * */); X X progName = argv[0]; X if (argc < 2) { X /* no parms, must be a request for information */ X <Provide Usage> X exit(0); X } X else if (argc == 2) { X /* one parm, make names the same */ X fromName = argv[1]; X toName = segmentPart(argv[1]); X } X else if (argc == 3) { X /* two parms, make second one the new name */ X fromName = argv[1]; X if (strcmp(argv[2],".")==0) { X /* the directory */ X toName = segmentPart(fromName); X } X else { X toName = segmentPart(argv[2]); X } X } X X.fi X.ec X.PP XNote, please that toName is always the segment (filename) part of Xthe desired location. This prevents one from saying "append x ../x" Xand making x appear in some directory you don't want append permissions Xapplied to. X X.nf X.eo X X <Validity Check> X rc = link(fromName,toName); X <Completion Check> X exit(0); X} X X.B X.br X<Globals>= X.R X.eo X.nf X#define ERR (-1) X X X.fi X.ec X.NH 2 XChecking the Advisability of Appending X.br X.PP XIt is inadvisable to append many kinds of file to a directory, Xsuch as directorys and special files, and of course the file Xshould be present and either readable or executable. XThis block checks for accessibility and plausibility. X X.PP XPlease note that it is reasonable to append a setuid Xor setgid program to a directory. In fact, this is the proper Xway to place a "gate" to certain private or sensitive Xinformation in a generally accessible place. It is not Xreasonable to contrive to chown and re-setuid a program Xto belong to root and append it to a directory, but this is dealt Xwith by chown and chmod directly, and does not affect append. X.PP XThis does not mean that giving anyone append permissions on /bin is Xa good idea: it is not hard to write a program which contains a Xtrapdoor to catch the superuser, and append on /bin or /usr/bin Xwould make it easy to put it in his way. X.PP XIn general, one places append permissions on directories like X/usr/local/bin (which superuser doesn't normally search for commands), Xman-page directories and transfer directories. XUucppublic would have been an excellent Xexample if the uucp author had thought of this technique... X X.PP XOne can append a FIFO to a directory even though it Xis a type of "special" file, since this does not constitute an X(obvious) security problem. X X.B X.br X<Validity Check>= X.R X.eo X.nf Xif (stat(progName,&s) == ERR || s.st_mode & (DIRECTORY|SPECIAL) X || (s.st_uid != geteuid() && s.st_gid != getegid())) { X /* someone's trying to trick me by putting append in his path */ X fprintf(stderr,"%s: Can't append to current directory, %s\n", X progName, "\"cd\" to target directory first"); X exit(1); X} X X Xif (stat(fromName,&s) == ERR) { X switch (errno) { X case ENOTDIR: X fprintf(stderr,"%s: Can't access %s, %s.\n", progName, X fromName, "part of the path is a non-directory"); X break; X case ENOENT: X fprintf(stderr,"%s: File %s doesn't exist.\n", X progName,fromName); X break; X case EACCES: X fprintf(stderr,"%s: Can't search a directory in %s.\n", X progName,fromName); X break; X default: X fprintf(stderr,"%s: Can't link, error is \"%s\".\n", X progName,sys_errlist[errno]); X } X exit(1); X} Xelse if ((m=s.st_mode) & SPECIAL) { X fprintf(stderr,"%s: Can't append a special file.\n",progName); X exit(1); X} Xelse if (m & DIRECTORY) { X fprintf(stderr,"%s: Can't append a directory.\n",progName); X exit(1); X} X Xif (stat(toName,&s) != ERR) { X fprintf(stderr,"%s: Can't replace an existing file\n", X progName); X exit(1); X} X X.fi X.ec X.PP XThe block depends upon the following declarations and includes: X X.B X.br X<Includes>= X.R X.eo X.nf X#include <stdio.h> X#include <errno.h> X#include <sys/types.h> X#include <sys/stat.h> X X.B X.br X<Declarations>= X.R X.eo X.nf Xstruct stat s; /* stat buffer */ Xint m; /* file access mode */ X X.B X.br X<Globals>= X.R X.eo X.nf X#define DIRECTORY 0040000 X#define SPECIAL (0020000 | 0060000) X X X.fi X.ec X.NH 2 XChecking the Results of Appending X.br X.PP XThe link can fail for several reasons, notably because Unix Xcan't do cross-device links, link to a read-only file system Xand so forth. X X.B X.br X<Completion Check>= X.R X.eo X.nf Xif (rc == ERR) { X switch (errno) { X case EACCES: X fprintf(stderr,"%s: Cannot write to this directory (%s).\n", X progName, "Can't happen, send mail to the owner"); X exit(3); X case EXDEV: X fprintf(stderr,"%s: Can't do a cross-device link.\n", X progName); X exit(1); X case EROFS: X fprintf(stderr,"%s: Can't link to a r/o file system.\n", X progName); X exit(1); X case EMLINK: X fprintf(stderr,"%s: Can't link, too many already exist.\n", X progName); X exit(1); X case ENOSPC: X fprintf(stderr,"%s: Can't link, directory full.\n", X progName); X exit(1); X case ENOTDIR: X case ENOENT: X case EEXIST: X case EPERM: X fprintf(stderr,"%s: Can't link, impossible error \"%s\".\n", X progName,sys_errlist[errno]); X exit(3); X default: X fprintf(stderr,"%s: Can't link, error is \"%s\".\n", X progName,sys_errlist[errno]); X exit(2); X } X} X X.fi X.ec X.PP XThese depend in turn on the following: X.B X.br X<Declarations>= X.R X.eo X.nf Xextern int errno; Xextern char *sys_errlist[]; Xint rc; X X X.fi X.ec X.NH 2 XUser Header X.br X X.B X.br X<Header>= X.R X.eo X.nf X/* X * append -- a command to append a file to a directory to which X * one does not have write permission, using setgrp & ln. X * Placing append in a directory simulates the "sa a *.*" X * access control command of Multics. See also append.web. X */ X X.B X.br X<Provide Usage>= X.R X.eo X.nf Xfprintf(stderr,"%s -- add (via link) a file to this directory, %s\n", X progName, "even if you lack permission."); Xfprintf(stderr,"Usage: %s filename [newname]\n",progName); X X X.fi X.ec X.NH XUtility Routines X.br X.PP XThis program has a single utility routine, "segmentPart", which Xextracts the segment/filename part of a pathname string, by returning Xeverything right of the last "/". X X.B X.br X<Utilities>= X.R X.eo X.nf X char * XsegmentPart(s) char *s; { X char *p, *strrchr(/* char *, char */); X X if (s && (p=strrchr(s,'/'))) { X return ++p; X } X return s; X} X !-E-o-F