1 TITLE: More control and package management using package users (v0.8)
2 LFS VERSION: 3.1 (should work with later versions with minor changes)
3 AUTHOR: Matthias S. Benkmann <m.s.b@gmx.net>
6 -You want to know which packages your files belong to ?
7 -You want to deinstall software that doesn't have make uninstall ?
8 -You are bothered by programs installed setuid root behind your back ?
9 -You don't want packages to overwrite files from other packages ?
10 -You don't like package managers like RPM ?
11 -YOU WANT TOTAL CONTROL USING ONLY UNIX BUILTINS ?
15 LEGALESE: The author does not take responsibility for any damage of any kind
16 caused directly or indirectly by applying the methods described below.
17 There is no warranty of any kind, not even the implied warranty of fitness for
18 the purposes mentioned in the text, on any of the programs/scripts/commands
19 listed below and the author may not be held liable for any damage of any kind
20 caused by the execution of said programs/scripts/commands.
21 Proceed at your own risk!
23 ###########################################################################
25 ###########################################################################
27 2002-01-30 -added Changelog
28 -moved "chown 0.10000 `cat /tmp/installdirs`" command up (before
29 glibc package user is created)
30 -add_package_user: create home directory with "mkdir -p"
31 use $grpfile everywhere instead of /etc/group
32 -improved mammoth sentence in Introduction
33 -added note about possibility to have user name==group name
34 -source bashrc_basic in bashrc_package
35 -minor textual changes
38 -added section "Security issues with NFS"
41 2002-03-16 -added note, that on Linux make doesn't need to be setgid kmem
44 -changed LFS VERSION header to be more conservative
45 -added <br> tags to the synopsis for the sake of the hints
47 -added group mmedia to the list of suggested groups
52 ###########################################################################
54 ###########################################################################
56 Let's say I have written a program that you would like to use. To make it
57 easier for you I come over to install it for you. Would you give me the root
58 account and then leave the room ? No ? Then why do you give it to complete
59 strangers who you have never seen in your life, to install software packages
60 pulled from some Internet server, that come with no warranty and don't even
61 list their contents in the README, although they will happily spread them all
63 It is a mystery why Unix admins who wouldn't even trust their employer with
64 more than a normal user account carelessly execute complex and incomprehensible
65 installation scripts with full root rights.
67 Users and groups are the basic security principle in a Unix system. They have
68 been used successfully for a long time to control who has created a file and
69 who is allowed to delete or change this file. But this control has only been
70 imposed on the files of ordinary users. What a waste! I suggest to extend
71 this control to all system files.
74 #############################################################################
76 #############################################################################
78 The basic idea of this scheme is easily explained. Every package belongs to a
79 certain "package user". This is a special user account whose HOME directory
80 is /usr/src/<package> (or wherever you keep the tarballs for the package).
81 Usually this account has no password so that only root can su to a package
82 user, which ensures that package users do not open an additional way into the
83 system and undermine security.
84 Of course you *may* set passwords anyway to allow a co-admin who you want to
85 be able to install and maintain certain software packages to do so without
86 having access to the actual root account. This co-admin could for instance
87 install, delete, change additional libraries which might be necessary for his
88 workgroup. He would be unable, though, to remove or modify libraries which
89 don't belong to him/her, such as libc.
91 Every package user has a primary group that reflects the purpose of the
92 package, such as "system" for essential packages, "devel" for packages
93 related to software development and so on. There are other useful
94 organizational schemes, of course. You might want to have the group reflect
95 the workgroup of the above-mentioned co-admin who installed the file for
96 instance. Another possibility is to create a new group for every package
97 user with the same name as the user name. That way, even if a file needs to
98 be setuid root (and consequently has to be owned by root), the group of the
99 file tells you about the package where it originates.
101 In addition to the primary group, there is the secondary group which is the
102 same for all package users. This is the "install" group. All directories
103 that packages are allowed to install stuff in belong to the install group.
104 This includes directories such as /bin and /usr/bin but excludes directories
105 like /root or /. The directories owned by the install group are always
106 group-writeable. Without further preparation this does not give added security
107 or control because every package could replace the files from a different
108 package (the change would be visible in the output from ls -la, though).
109 For this reason we make all install directories sticky. The sticky attribute
110 allows users to create new files and delete or modify their own files in
111 the directory while preventing them from writing or deleting files from other
115 ############################################################################
116 How to install software
117 ############################################################################
119 Isn't this all very tedious ? Not if you use this script:
121 -------------------- begin $LFS/sbin/install_package ------------------------
123 if [ $# -ne 3 ]; then
124 echo USAGE: install_package description name group
125 echo "!!Don't forget to put description in quotes if it contains spaces.!!"
126 echo "This will create a new package user name in group. This will set up"
127 echo "a home directory for this package user. After that, this script will"
128 echo "automatically su to the new user so that you can begin with the"
129 echo "installation right away. "
133 if [ $UID -ne 0 ]; then echo Please run this script as root. ; exit 1; fi
134 add_package_user "${1}" $2 10000 20000 $3 10000 20000 || exit 1
136 ------------------- end $LFS/sbin/install_package ----------------------------
138 The above script is a shortcut for the following script, which does the
139 actual adding of the package user in a certain range of UIDs and GIDs:
141 -------------------- begin $LFS/sbin/add_package_user -----------------------
144 if [ $# -lt 7 ]; then
145 echo 'Copyright (c) 2000 Matthias Benkmann'
147 echo 'add_package_user description name minuid maxuid group mingid maxgid [-d home]'
149 echo Don\'t forget to put description in quotes.
150 echo description must be valid for a /etc/passwd entry, that means especially
151 echo that it must not contain \":\".
152 echo If group doesn\'t exist, it will be added.
153 echo group will be the user\'s main group.
154 echo 'The user will also belong to group "install" (created if necessary).'
155 echo 'A home directory /usr/src/name (or home if -d home specified)
156 echo will be created if it doesn\'t exist
157 echo 'and the files from /etc/skel-package will be copied into it (existing'
158 echo 'target files will *not* be overwritten)'
159 echo 'minuid (incl.) and maxuid (excl.) determine'
160 echo 'the range of UIDs used by this script. The script will pick the first'
161 echo 'UID greater than all UIDs from this range already in use.'
162 echo 'If that UID is out of range (i.e. equal to maxuid), the'
163 echo 'script will pick the first available UID in this range. If the range is'
164 echo 'full, the script will give up. This process ensures that UIDs for packages'
165 echo that have been removed, don\'t get reassigned unless really necessary.
166 echo 'This avoids trouble if some files were overlooked when removing the package.'
167 echo 'Otherwise these files might be assigned to the wrong package.'
168 echo 'mingid (incl.) and maxgid (excl.) define'
169 echo 'the permissible range for the GID of group if it has to be added (and '
170 echo 'the install group if it must be added).'
177 skel=/etc/skel-package
187 set -- "$@" _eNd_OF_lisT_
188 while [ "$1" != "_eNd_OF_lisT_" ]; do
191 if [ "$1" = "_eNd_OF_lisT_" ]; then
192 echo 1>&2 "-d directory name missing!"
204 shift 1 #throw away _eNd_OF_lisT_
206 if [ $UID -ne 0 ]; then echo Please run this script as root. ; exit 1; fi
208 #test if user already exists
209 grep "^$name:.*" $passwd
210 if [ $? -eq 0 ]; then
211 echo 'Package user does already exist! Do su '$name' to do maintenance work.'
215 #test if minuid, maxuid, mingid and maxgid are integers, otherwise set
218 expr ${minuid} + 1 2>/dev/null 1>&2 || error=1
219 expr ${maxuid} + 1 2>/dev/null 1>&2 || error=1
220 expr ${mingid} + 1 2>/dev/null 1>&2 || error=1
221 expr ${maxgid} + 1 2>/dev/null 1>&2 || error=1
223 if [ $error -eq 1 ]; then
224 echo Error: Illegal numeric value!
228 if [ $minuid -ge $maxuid ]; then
229 echo 'Error: minuid must be less than maxuid !'
233 if [ $mingid -ge $maxgid ]; then
234 echo 'Error: mingid must be less than maxgid !'
239 uidlist=`cut -d : -f 3 $passwd | sort -n`
241 #find last used UID within range
245 if [ $i -ge $maxuid ]; then break; fi
246 if [ $i -ge $minuid ]; then u=$i; fi
249 #if no UID from the range is used, pick the first, otherwise pick the one
250 #immediately following the last UID in use.
251 if [ $u -eq 0 ]; then u=$minuid; else u=`expr $u + 1`; fi
253 #if the last UID used from the range is maxuid-1, i.e. we may
254 #not use its successor as UID, then we look for the first unused uid
256 if [ $u -ge $maxuid ]; then
260 if [ $u -eq $i ]; then u=`expr $u + 1` ; fi
261 if [ $i -ge $maxuid ]; then break; fi
264 if [ $u -ge $maxuid ]; then
265 echo Error: UID range is full!
270 echo Will create user $name with uid: $u
274 #############################################################################
276 #############################################################################
278 #execute the following for gname and "install" to get gids for those 2 groups
282 for group in install $gname
284 oldg=$g #save gid from previous run
285 createinstall=$creategroup
288 #test if group already exists and extract gid if so
289 g=`grep ^${group}:.\* $grpfile | cut -d : -f 3 -`
291 #if group does not exist, then check range for a free gid
295 gidlist=`cut -d : -f 3 $grpfile | sort -n`
297 #find last used GID within range
301 if [ $i -ge $maxgid ]; then break; fi
302 if [ $i -ge $mingid ]; then g=$i; fi
305 #if no GID from the range is used, pick the first, otherwise pick the one
306 #immediately following the last GID in use.
307 if [ $g -eq 0 ]; then g=$mingid; else g=`expr $g + 1`; fi
309 #don't reuse gid from previous run
310 if [ $g -eq $oldg ]; then g=`expr $g + 1`; fi
312 #if the last GID used from the range is maxgid-1, i.e. we may
313 #not use its successor as GID, then we look for the first unused gid
315 if [ $g -ge $maxgid ]; then
319 if [ $g -eq $i ]; then g=`expr $g + 1` ; fi
320 if [ $g -eq $oldg ]; then g=`expr $g + 1` ; fi
321 if [ $i -ge $maxgid ]; then break; fi
324 if [ $g -ge $maxgid ]; then
325 echo Error: GID range is full!
334 if [ $createinstall -eq 1 ]; then
335 echo Creating group install with gid $oldg
336 groupadd -g $oldg install || exit 1
338 echo Group install has gid $oldg
340 if [ $creategroup -eq 1 ]; then
341 echo Creating group $gname with gid $g
342 groupadd -g $g $gname || exit 1
344 echo Group $gname has gid $g
349 useradd -c "${description}" -d ${home} -g ${gname} -G install \
350 -s /bin/bash -u ${u} ${name} || exit 1
352 mkdir -p $home || exit 1
354 yes n|cp -ai -R ${skel}/{[^.],.[^.],..?}* ${home} 2>/dev/null >/dev/null
357 chown --recursive ${u}.${g} .
361 -------------------- end $LFS/sbin/add_package_user ------------------------
364 If you use these scripts, you can install a new package like this
366 install_package 'Foo description' foo foogroup
368 and the package user foo will be created and you will automatically be su'd to
369 it and put into the package user's home directory. Now just copy the tarball
370 from your download directory, untar and install. It's no more work than
374 ############################################################################
375 Pitfalls and Solutions
376 ############################################################################
378 There are some pitfalls when you use this system. The following situations are
381 1. A package install script is poorly written and insists on changing
382 ownership/permissions of system directories or its own files.
384 2. A package install script has a legitimate reason to change
385 ownership/permissions of files it installs.
387 3. Package A contains a program that is also part of package B. Package B
390 4. A package tests if the user installing it is root and does not install
391 certain programs otherwise.
393 5. A package creates a directory that other packages need to write to.
395 1-3 usually result in an "Operation not permitted" error during make install.
396 Note that this is not actually a flaw of the package user scheme, it's a
397 feature. It's the reason why we made the install directories sticky and use
398 package users in the first place. We don't want stuff like this to happen
399 without our (divine :-) intervention. Fixing these issues is usually easy:
401 1. Look at the output from "make install". It will contain the offending
402 command such as "install --owner root foo /bin" right before the
403 "Operation not permitted" error. grep through the Makefiles of the package
404 and remove the offending switch (in this case "--owner foo").
405 Sometimes you may not want to remove it altogether but may need to change
406 it to something harmless. For instance "install -m 4755 ..." which tries to
407 set the setuid bit should be changed to "install -m 755 ...".
408 You can automate these changes with a sed script or use a wrapper script
409 around install (see below) to avoid having to manually change Makefiles.
410 Note that you either have to perform these changes *after* the configure
411 step or you have to modify the source Makefiles (usually Makefile.in or
414 2. This is basically like 1 but in this case the package actually has a reason
415 (that you accept) for doing what it does. This is seldom but happens for
416 some packages that install setuid root programs that you want to have
417 setuid root, such as ping. In this case, act like in case 1 and execute
418 the appropriate changes manually as root afterwards,
419 such as chown root.root /bin/ping && chmod 4755 /bin/ping.
421 3. In this case you have to decide which package you actually want to provide
422 the program in question. If you want to replace package B's program with
423 that of package A, simply delete the program manually as root.
424 If you don't want package A to overwrite the program with its own,
425 grep through the Makefiles for the name of the program in question (and
426 its manpages). Usually you will find a list like
427 PROGRAMS=foo bar fubar barfu
428 and it is sufficient to remove the program from this list. In very rare
429 cases you may need to remove some "install <programname> <destination>"
430 lines from the Makefile.
432 4. This case is usually documented in the INSTALL file of the package and a
433 make target to install the programs left out, such as "install-root", is
434 provided. So this is not normally an issue.
436 5. This case is easy to fix by assigning the directory in question to the
437 install group and making it group-writeable and sticky. This does not even
438 have to be done as root. The package user who created the directory has the
439 permissions to do it. So you can insert an appropriate line into the
442 Manually changing Makefiles is annoying, even if it happens only occasionally.
443 Sed scripts do help but they have to be custom fitted to every package that
444 needs them. There is a better solution, though. While building LFS with the
445 package user scheme, I have noticed that although there are a lot of possible
446 things a Makefile could contain which would trigger an
447 "Operation not permitted" error, only few of them are actually used.
448 So far I have only encountered these problems in connection with mkdir, chgrp,
450 I have written the following scripts to handle them automatically. If you make
451 sure the directory where these scripts are located (in my case /usr/src/lfs)
452 is the first in the PATH for every package user, you should be able to install
453 most LFS packages without having to fix any Makefiles manually.
455 ATTENTION! These wrapper scripts allow "make install" to complete without
456 aborting due to "Operation not permitted" errors. However, some of the
457 operations that are suppressed this way are legitimate (see 2. above).
458 For that reason, the wrapper scripts output lines beginning with "***"
459 to stderr that contain the original command that the installation wanted to
461 You *must* check these lines and if it was a legitimate operation you have to
462 take appropriate action manually or the programs in question will not
463 work properly. The following is a list of things you should look for and
466 1. "*** install -m 4xxx -o root" : This wants to make a binary setuid root.
467 The important thing here is "-m 4x". Many install scripts try to use
468 "install -m 755 -o root" or something similar. This can be ignored. The
469 owner (specified with "-o owner") is only important for binaries with mode
470 4xxx (specified with "-m 4xxx"). If you decide that the binary really
471 should be setuid root (for the LFS base packages this is usually okay)
473 chown root.root <binaryname>
474 chmod u+s <binaryname>
476 2. "*** chgrp xxx" : This wants to change the group of a binary. This is
477 usually only done if a binary is made setgid. You will see that in the
478 output of "ls -l" like this
479 rwxr-sr-x 1 util-lin tty 8348 Dec 3 2000 write
480 The "s" in the group field tells you this is a setgid binary.
481 Right now the chgrp wrapper below only intercepts chgrp for xxx=tty
482 because that is the only thing needed for installing LFS.
483 If such a call has been intercepted and you want the binary to be setgid,
484 you have to do the following:
485 chgrp xxx <binaryname>
486 chmod g+s <binaryname>
489 It is important that you check the error log religiously when installing
490 a package as a package user, especially if you use the scripts below.
492 To make this easier, install like this
494 make install 3>&1 1>&2 2>&3 | tee install.err
496 This will give you the normal make output (i.e. errors and normal messages)
497 to the screen but in addition to that it will put a copy of all error
498 messages into install.err. You can "cat install.err" after the installation
499 to examine error messages.
500 Note that "make install | tee install.err" does *not* work. This would
501 copy all normal messages *without* the errors.
503 If you want to redirect screen output (i.e. the complete install log,
504 including errors and non-errors) to a file, do it like this
506 { make install 3>&1 1>&2 2>&3 | tee install.err ;} &>install.log
508 You should probably create a shell function for this to save you typing.
511 Here are the wrapper scripts:
513 ------------- begin $LFS/usr/src/lfs/wrappers/mkdir --------------------------
518 for p in $(type -ap mkdir) ; do
519 if [ ! $p -ef $0 ]; then DAISY_CHAIN=$p ; break ; fi
522 if [ ! -n "$DAISY_CHAIN" ]; then
523 echo Cannot find real ${0##*/} command
527 if [ $UID == 0 ]; then
528 exec $DAISY_CHAIN "$@"
531 watchdir=/usr/share/locale
541 $watchdir/*) dirs="$dirs ""`expr $a : "$watchdir/\(.*\)"`"
544 *) set -- "$@" "$a" ;;
548 $DAISY_CHAIN "$@" || exit $?
550 test z"$dirs" != z &&
551 echo 1>&2 '***' mkdir "$cmdline"
552 for dir in $dirs ; do
554 for d in `echo $dirs | sed 's#/# #g' -` ; do
555 cumuldir=$cumuldir$d/
556 chgrp install $watchdir/$cumuldir
557 chmod g+w,o+t $watchdir/$cumuldir
561 ------------ end $LFS/usr/src/lfs/wrappers/mkdir --------------------------
563 ------------ begin $LFS/usr/src/lfs/wrappers/chgrp ------------------------
568 for p in $(type -ap chgrp) ; do
569 if [ ! $p -ef $0 ]; then DAISY_CHAIN=$p ; break ; fi
572 if [ ! -n "$DAISY_CHAIN" ]; then
573 echo Cannot find real ${0##*/} command
577 if [ $UID == 0 ]; then
578 exec $DAISY_CHAIN "$@"
581 if [ "$1" == "tty" ]; then
582 echo 1>&2 '***' chgrp "$@"
584 $DAISY_CHAIN "$@" || exit $?
588 ------------ end $LFS/usr/src/lfs/wrappers/chgrp --------------------------
590 ------------ begin $LFS/usr/src/lfs/wrappers/chown ------------------------
595 for p in $(type -ap chown) ; do
596 if [ ! $p -ef $0 ]; then DAISY_CHAIN=$p ; break ; fi
599 if [ ! -n "$DAISY_CHAIN" ]; then
600 echo Cannot find real ${0##*/} command
604 if [ $UID == 0 ]; then
605 exec $DAISY_CHAIN "$@"
608 if [ "$1" == "root.root" ]; then
609 echo 1>&2 '***' chown "$@"
611 $DAISY_CHAIN "$@" || exit $?
615 -------------- end $LFS/usr/src/lfs/wrappers/chown ------------------------
617 ------------ begin $LFS/usr/src/lfs/wrappers/install ----------------------
620 localedir=/usr/share/locale
622 manpagesowner=man-pages
626 for p in $(type -ap install) ; do
627 if [ ! $p -ef $0 ]; then DAISY_CHAIN=$p ; break ; fi
630 if [ ! -n "$DAISY_CHAIN" ]; then
631 echo Cannot find real ${0##*/} command
635 if [ $UID == 0 ]; then
636 exec $DAISY_CHAIN "$@"
640 #********** test if we create directories ********************
641 if [ \( z"$1" = z"-d" \) -o \( z"$1" = z"-m" -a z"$3" = z"-d" \) ]; then
650 -o|-g|--owner|--group) notify=1
654 $localedir/*) if [ ! -d "$a" ]; then
655 locdirs="$locdirs ""`expr $a : "$localedir/\(.*\)"`"
663 */*|/sbin) if [ ! -d "$a" ]; then
671 *) set -- "$@" "$a" ;;
675 test $notify -eq 1 -o z"$locdirs" != z && \
676 echo 1>&2 '***' install "$cmdline"
678 test $havedir -eq 0 && exit 0
680 $DAISY_CHAIN "$@" || exit $?
682 test z"$locdirs" != z &&
683 for dir in $locdirs ; do
685 for d in `echo $locdirs | sed 's#/# #g' -` ; do
686 cumuldir=$cumuldir$d/
687 if [ -d $localedir/$cumuldir ]; then
688 chgrp install $localedir/$cumuldir
689 chmod g+w,o+t $localedir/$cumuldir
694 else #if "$1" != "-d" ,i.e. we do not create directories *****************
705 4755) notify=1 ; set -- "$@" "755" ;;
706 *) set -- "$@" "$a" ;;
709 -m4755) notify=1 ; set -- "$@" "-m755" ;;
710 -o|-g|--owner|--group) notify=1
715 if [ -e "$a" -a ! -O "$a" ]; then
716 if [ `find "$a" -printf \%u` = $manpagesowner ]; then
718 set -- "$@" not_installed
726 *) set -- "$@" "$a" ;;
730 test $notify -eq 1 && echo 1>&2 '***' install "$cmdline"
732 $DAISY_CHAIN "$@" || exit $?
736 ----------- end $LFS/usr/src/lfs/wrappers/install --------------------------
738 If you improve any of these scripts, I would be grateful if you could mail
739 me your changes together with a note saying which package prompted the
740 change. Note that these scripts are supposed to have minimal intrusiveness.
741 So writing a common case "-m*)" instead of the case "-m4755" is undesirable.
742 The scripts should only deal with the problems that have actually been
743 encountered, not all possible problems.
745 #############################################################################
747 #############################################################################
749 For the user name of the package users I always use the name of the package
750 without the version number, including dashes and possibly exceeding 8
751 characters in length, e.g. "util-linux".
752 Aside from ls -l which chops off the last characters
753 of the user name I have not noticed any problems with this. The other programs
754 I have tested seem to deal with these user names just fine.
755 If you encounter problems, please report them to me.
757 Now how do we apply the above to the building of an LFS system ?
759 ##########################################################################
761 ##########################################################################
763 We don't use package users in the pre-chroot phase. This does not mean we
764 install as root. I suggest building the pre-chroot system as a normal user.
765 That way, you can later distinguish files created inside chroot (these
766 have uid 0 or the uid of a package user) and remaining files created during
767 the pre-chroot phase (these carry the uid of your normal user account on
768 your host system). Building as an ordinary user also protects you against
769 typos that might otherwise cause a package to overwrite files on your host
772 The actual commands of the pre-chroot phase don't need be changed but you
773 have to do some additional things.
774 The first problem is that we need find right after installing glibc inside
775 chroot. The best thing to do is to build a static version during the
776 pre-chroot phase like this:
778 ./configure --prefix=$LFS/usr --disable-nls &&
779 make LDFLAGS=-static CPPFLAGS=-Dre_max_failures=re_max_f2 &&
780 make libexecdir=$LFS/usr/bin install
782 Another problem is that some programs such as chown can not resolve usernames
783 before glibc is installed. Fortunately most programs accept a numeric UID
784 or GID. There is one nasty exception, though: su. The most fundamental
785 program when using package users doesn't swallow numeric ids :-|
787 Now how do we become a package user before glibc is installed ?
788 If you guessed that I have a written a script to replace it, you guessed ...
789 ... wrong :-) I have written a C program to replace it.
791 It just happens that sh-utils
792 contains an su program which is not used in LFS. It gets installed
793 statically linked during the pre-chroot phase, is never used there (assuming
794 a standard LFS build), gets replaced with a dynamically linked version when
795 sh-utils is reinstalled and then is finally replaced by the su from shadow
796 which is the version we will use when the LFS system is finished. So all
797 you need to do is to replace the file src/su.c in the sh-utils source tree
798 with the following file before compiling your chapter 5 sh-utils.
799 Note that if you compile chapter 5 as non-root, you need to issue the command
800 `make install-root' after `make install' or sh-utils won't install su.
802 -------- begin $LFS/usr/src/sh-utils/sh-utils-<version>/src/su.c ------------
804 #include <sys/types.h>
814 int main(int argc,char* argv[])
825 gid_t gid_list[NUMGIDS];
830 if (strcmp(argv[1],"--help")==0)
831 { fprintf(stdout,"There is no help!\n"); exit(0);}
832 if (strcmp(argv[1],"--version")==0) {fprintf(stdout,"0.6\n"); exit(0); }
835 if ((argc==4) && (strcmp(argv[2],"-c")==0)) command=argv[3]; else
838 fprintf(stdout,"USAGE: su username|uid [-c command]\n");
843 while(isdigit(argv[1][i])) ++i;
844 if (argv[1][i]==0) tuid=atol(argv[1]);
846 File=fopen("/etc/passwd","rb");
847 if (File==NULL) {perror("/etc/passwd"); return 1;};
852 Res=fgets(Buffy,1024,File);
854 if (errno!=0) perror("/etc/passwd");
855 else fprintf(stderr,"su: User not found!\n");
859 Res=strtok(Buffy,":");
860 if (Res==NULL) continue;
862 uid=atol(strtok(NULL,":"));
863 gid=atol(strtok(NULL,":"));
865 HOME=strtok(NULL,":");
866 shell=strtok(NULL,":");
867 if (tuid==uid) {argv[1]=strdup(Buffy); break;}
868 if (strcmp(argv[1],Buffy)==0) break;
873 File=fopen("/etc/group","rb");
874 if (File==NULL) {perror("/etc/group"); return 1;};
878 ContinueReadingEtcGroup:
880 Res=fgets(Buffy,1024,File);
882 if (errno!=0) {perror("/etc/group"); return 1;} else break;
885 Res=strtok(Buffy,":,\n");
886 if (Res==NULL) continue;
888 gid_list[NumGids]=atol(strtok(NULL,":,\n"));
889 Res=strtok(NULL,":,\n");
893 if (strcmp(Res,argv[1])==0)
896 if (NumGids>=NUMGIDS) goto SetNewIdentity;
897 else goto ContinueReadingEtcGroup;
899 Res=strtok(NULL,":,\n");
904 if (command==NULL) command=shell;
905 setenv("HOME",HOME,1);
906 setgroups(NumGids,gid_list);
911 if (((i<0) || (i==127)) && (errno!=0)) {perror("/bin/sh"); return 1;};
914 -------- end $LFS/usr/src/sh-utils/sh-utils-<version>/src/su.c ------------
916 This su program accepts user names as well as numeric UIDs. It does its own
917 name resolution using /etc/passwd so it works even without glibc being
919 Note that sh-utils installs the su program with the setuid bit set.
920 If you install the pre-chroot system as an ordinary user (which you should
921 for safety reasons), this will result in a su that doesn't work even when
922 executed by root. Simply remove the setuid bit like this
924 chmod u-s $LFS/bin/su
926 and su will work fine once we have entered the chroot environment (where
927 we work as root). Note that this allows you to su from root to a package
928 user but not the other way around. If you want to be able to su from a package
929 user to root you will have to make su setuid root
930 (i.e. chown root.root $LFS/bin/su && chmod u+s $LFS/bin/su). Make sure that
931 you don't keep such an su lying around on a system that others have access to.
932 The above su does not check for a password, so if you make it setuid root,
933 everyone can use it to become root without a password!
936 ##########################################################################
937 Chroot phase - preparing the LFS system:
938 ##########################################################################
940 This is the tricky part. Once we
941 have entered chroot, we have to assign install directories to the
942 install group and make them group-writeable. However, since glibc is not
943 yet installed, we can't use user names, yet. Furthermore, groupadd and useradd
944 which are part of the shadow package are not installed, yet.
945 This means that we can't create any package users, at least not without
946 editing /etc/group and /etc/passwd manually.
947 But don't despair. The following
948 scripts make nice replacements for useradd and groupadd as long as shadow
949 isn't there. Note that they only accept the exact syntax with which the
950 useradd and groupadd commands are used in the add_package_user script.
951 I assume you will be using the install_package/add_package_user scripts to
952 add package users to the system. Here are the scripts:
954 --------------------- begin $LFS/usr/sbin/useradd ----------------------------
956 if [ $# -ne 13 -o z$1 != z-c -o z$3 != z-d -o z$5 != z-g -o z$7 != z-G -o z$9 != z-s -o z${11} != z-u ]; then
957 echo 1>&2 USAGE: useradd -c description -d home -g maingroup -G addgroup -s shell -u uid login
961 #test if user already exists
962 grep "^${13}:.*" /etc/passwd
963 if [ $? -eq 0 ]; then
964 echo 1>&2 $0: User does already exist
968 g=`grep ^${6}:.\* /etc/group | cut -d : -f 3 -`
969 if [ z${g} = z ]; then
970 echo 1>&2 $0: Group ${6} does not exist!
974 grep ^${8}:.\* /etc/group >/dev/null || \
976 echo 1>&2 $0: Group ${8} does not exist!
981 cp /etc/passwd /tmp/passwd123456
982 echo "${13}:x:${12}:$g:$2:$4:/bin/bash" \
983 | sort -t : -k3,3n -m /tmp/passwd123456 - > /etc/passwd
986 cp /etc/group /tmp/group123456
987 sed -e 's/^\('"${8}"':[^:]*:[0-9]*:..*\)$/\1,'"${13}"'/' \
988 -e 's/^\('"${8}"':[^:]*:[0-9]*\):$/\1:'"${13}"'/' \
989 /tmp/group123456 >/etc/group
990 ---------------------- end $LFS/usr/sbin/useradd ----------------------------
992 --------------------- begin $LFS/usr/sbin/groupadd --------------------------
994 if [ $# -ne 3 -o z$1 != z-g ]; then
995 echo 1>&2 USAGE: groupadd -g gid groupname
999 #test if group already exists
1000 grep "^${3}:.*" /etc/group
1001 if [ $? -eq 0 ]; then
1002 echo 1>&2 $0: Group does already exist
1006 cp /etc/group /tmp/group123456
1007 echo ${3}:x:${2}: | sort -t : -k3,3n -m /tmp/group123456 - > /etc/group
1008 --------------------- end $LFS/usr/sbin/groupadd ----------------------------
1010 These scripts overcome the problem of the missing shadow utilities.
1012 With the above scripts and su program we now have everything we need to
1013 begin our chroot phase.
1015 ###########################################################################
1016 Tips before you chroot
1017 ###########################################################################
1019 Whenever add_package_user creates a new package user it copies the files
1020 from /etc/skel-package (if you create this directory) to the new package
1021 user's home directory. Symlinks are copied as symlinks.
1022 A useful symlink to place in /etc/skel-package is one like
1024 .bashrc -> /etc/bashrc_package
1026 with something like the following:
1028 ------------------ begin $LFS/etc/bashrc_package --------------------------
1029 #first load basic configuration to make system work normal
1030 source /etc/bashrc_basic
1032 export PATH=/usr/src/lfs/wrappers:$PATH
1034 #make prompt reflect that we are a package user. Do it via USER_PROMPT_COMMAND
1035 #rather than PROMPT_COMMAND because we want to keep root's prompt if we su to root
1036 export USER_PROMPT_COMMAND='PS1="package \u:"`pwd`"> "'
1038 alias mafobu='make -f /usr/src/lfs/mafobu'
1040 #The following command will put us in the home directory.
1042 ------------------ end $LFS/etc/bashrc_package ----------------------------
1044 ------------------ begin $LFS/etc/bashrc_basic ----------------------------
1045 #This file should be sourced by all users' .bashrc files
1047 if [ $UID -eq 0 ]; then
1048 export PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin
1050 export PATH=/usr/local/bin:/bin:/usr/bin
1053 #set a red prompt when user is root. If a user wants to override this
1054 #s/he should use USER_PROMPT_COMMAND instead of PROMPT_COMMAND in order to
1056 #root's prompt when su'ed to root. If a user wants to change PS1 directly
1057 #s/he should do "unset USER_PROMPT_COMMAND".
1058 export PROMPT_COMMAND='if [ $UID -eq 0 ]; then \
1059 PS1="\[\033[0;31m\]root@\h:"`pwd -P`"# \[\033[0m\]" ; \
1060 else eval $USER_PROMPT_COMMAND ; fi'
1061 #make PROMPT_COMMAND read-only to protect against careless users
1062 declare -r PROMPT_COMMAND
1063 #set a reasonable default USER_PROMPT_COMMAND
1064 export USER_PROMPT_COMMAND='PS1="\u@\h:"`pwd`"> "'
1066 #make keys (del, bs, home, end,...) work normal
1067 export INPUTRC=/etc/inputrc
1069 ------------------ end $LFS/etc/bashrc_basic ------------------------------
1073 Another useful file to place in /etc/skel-package is a .project file like
1076 ------------------ begin $LFS/etc/skel-package/.project ------------------
1084 ftp://ftp.gnu.org/gnu/foo/
1089 ----------------- end $LFS/etc/skel-package/.project ------------------
1091 Update this file whenever you re/install a package. It is called .project
1092 so that it is automatically displayed when you issue the command
1093 "finger <package>" or "pinky -l <package>".
1096 ###########################################################################
1097 Inside the chroot environment
1098 ###########################################################################
1100 After you have chroot'ed do
1102 groupadd -g 10000 install
1104 which will create the install group. Now use
1106 chown 0.10000 `cat /tmp/installdirs`
1107 chmod ug=rwx,o=rx `cat /tmp/installdirs`
1109 to assign directories to the install group. Note that we do not make the
1110 directories sticky, yet. This is because they still contain files belonging
1111 to root or some unknown user (if you installed pre-chroot as non-root). These
1112 files must be overwriteable. The file /tmp/installdirs is the following list:
1114 ----------------- begin $LFS/tmp/installdirs ------------------------------
1150 /usr/local/share/dict
1151 /usr/local/share/doc
1152 /usr/local/share/info
1153 /usr/local/share/locale
1154 /usr/local/share/man/man1
1155 /usr/local/share/man/man2
1156 /usr/local/share/man/man3
1157 /usr/local/share/man/man4
1158 /usr/local/share/man/man5
1159 /usr/local/share/man/man6
1160 /usr/local/share/man/man7
1161 /usr/local/share/man/man8
1162 /usr/local/share/nls
1163 /usr/local/share/misc
1164 /usr/local/share/terminfo
1165 /usr/local/share/zoneinfo
1170 ----------------- end $LFS/tmp/installdirs --------------------------------
1174 chown 0.10000 /usr/share/info/dir
1175 chmod ug=rw,o=r /usr/share/info/dir
1177 which makes sure packages can install their info pages.
1181 #########################################################################
1182 Chroot phase -installing the packages
1183 #########################################################################
1185 Now we can finally install glibc. Create /dev/null as root by doing
1187 mknod -m 0666 /dev/null c 1 3
1191 install_package "GNU C library" glibc system
1193 which will create the system group and a user glibc and will also su to the new
1194 glibc package user. Note that the mkdir and install wrapper
1195 don't work properly during the installation of glibc because they use
1196 the name "install" rather than the gid. This means we have to do the
1197 following *after* installing glibc:
1199 find /usr/share/locale/* -type d -user glibc -exec chmod ug=rwx,o=rxt \{\} \;\
1200 -exec chgrp install \{\} \;
1207 to become root and begin installing the rest of the packages. Use the command
1209 install_package <description> <packagename> <group>
1211 to create and become the package user. Then unpack in that user's home
1212 directory and use the guidelines below and the book's directions to
1213 install each package.
1215 ############################################################################
1217 ############################################################################
1219 I recommend the following groups for use with package users:
1221 devel: development related stuff, e.g. compilers. This is not restricted to
1222 software development. TeX for instance would belong in this group.
1224 utils: Most software fits into this category, even somewhat essential software
1225 like grep or text editors.
1227 net: network related stuff such as an ftp daemon or a web browser. This
1228 group overlaps with other groups to a large extent. It should be used
1229 in preference of the other groups whenever a package is clearly focused
1230 towards Internet, LAN, WWW,... A utility like wget for instance would
1231 go in net rather than utils. Exceptions from this rule are the groups
1232 docs, addons, games and mmedia. If a package fits into one of those
1233 groups, use the respective group instead of net.
1235 docs: Documentation related packages, such as a tarball with Linux howtos.
1236 Note that software to create documentation such as XML processors should
1237 probably go in devel and software to view or post-process documentation
1238 such as man or groff should probably go in utils.
1240 system: important system software, such as bash. This group should be used
1241 only for really essential packages. Most packages you would put in
1242 this group are better put into "utils". Vi for instance belongs in
1244 It is unlikely that any package not part of basic LFS belongs in the
1247 libs: What utils is for executables, libs is for libraries. Libraries that are
1248 not strongly related to any of the other categories should go here, such
1250 Essential system libraries such as glibc, ncurses or gettext should
1251 go in system instead.
1252 The libs group is also used for run-time environments such as the
1253 Java Virtual Machine, dosemu and wine. Other emulators like MAME for
1254 instance should probably go into games instead.
1256 games: what do you expect ;-)
1258 mmedia: This is the group for audio and video editors, mp3 players etc.
1260 apps: Applications such as spreadsheets and word processors (not text editors)
1261 but also CAD software and graphics software such as Gimp.
1262 The apps group is a bit like utils, but apps are usually more user
1263 friendly and more streamlined and focused than utils.
1264 Apps feel less geekish and nerdish than
1265 utils. Emacs for instance belongs in utils, not in apps.
1267 addons: plugins, filters and similar that are meant to be used in conjunction
1268 with another package.
1270 x: software that relates to the X Window System in general and does not fit
1271 into any of the other categories, such as the X server itself or window
1273 Most X software should be put into other more specific groups.
1274 A game like xmines would go in games for instance and a text editor for
1275 X would go in utils.
1277 kde: Software that relates to KDE and does not fit into
1278 any other category. This group should be used with care.
1279 Do *not* use it for all KDE software. K Office for instance belongs in
1280 apps. Konqueror belongs in net.
1282 gnome: Software that relates to GNOME and does not fit into
1283 any other category. This group should be used with care.
1284 Do *not* use it for all GNOME software. Gimp for instance belongs
1285 in apps. A GNOME-aware window manager that works with plain X should
1288 The following is a list of the LFS packages with the user.group assignments
1289 I think are appropriate. The user name in general should be the name of
1290 the main tarball without extensions and version. The groups try to follow
1291 the guidelines above but sometimes it is hard to find the "correct" group.
1292 The list also contains some notes I made when I installed them.
1293 If you use the mkdir, chgrp and install wrappers, you should
1294 not have any trouble.
1299 MAKEDEV: not installed as a package user
1304 rm /usr/bin/mawk /usr/share/man/man1/mawk.1
1305 as root before `make install'
1310 Before `make install' assign the /usr/lib/gcc-lib and /usr/include/g++
1311 to the gcc package user by doing (as root)
1312 chown -R gcc. /usr/lib/gcc-lib /usr/include/g++
1322 Creates the directory /usr/share/aclocal. This directory is written to
1323 by other packages, so chgrp install and chmod g+w,o+t it.
1326 When ./configure is done as a package user (more precisely when done
1327 in a su environment), it will fail to detect /dev/stdin. This will
1328 cause bash to compile an emulation of /dev/std* and /dev/fd/*.
1329 I actually prefer this emulation because it matches the description
1330 in the manpage while the normal behaviour (i.e. to open the
1331 respective device) does not always. If a normal bash is desired,
1332 use sed to change configure and replace
1333 "test -r /dev/stdin" with "test -e /dev/stdin" (same for /dev/fd).
1334 [ This information is for bash 2.05, I reported the issue so it may
1335 be fixed in future versions. ]
1351 make wants to be setgid kmem in order for the -l option to work.
1352 If you are not a developer who knows what this option does, you
1353 don't need it and should not give make special privileges.
1354 [UPDATE] On Linux the -l option seems to work even if make is not
1357 linux kernel: not installed as package user
1359 The modutils check if modules are owned by root before using them.
1360 This is the reason why the linux kernel should be compiled and
1361 installed as root, rather than as a package user.
1372 This package installs quite a few setuid root progs. If you're not
1373 religious about package users you might want to install this one
1374 as root.root to make life easier for yourself.
1375 If you do install as package user don't forget to make the
1376 binaries in question setuid root manually afterwards.
1377 After installing shadow make sure that the useradd and groupadd
1378 scripts have been properly replaced. Execute
1379 find / -maxdepth 3 -name groupadd
1380 to make sure that you don't have 2 copies (e.g. one in /sbin and
1385 This package has /usr/bin/install hardwired, so install with
1386 make INSTALL=install install
1387 to make it use the wrapper.
1390 This one is a bit tricky. You have to create /dev/initctl manually
1391 as root *before* doing "make install" because a package user does
1392 not have permission to write to /dev. So do (as root!!)
1394 mknod -m 600 /dev/initctl p
1399 Don't forget to make mount and umount setuid root manually after
1400 installing this package. And the write program wants to be
1403 ###########################################################################
1405 ###########################################################################
1407 After installing everything, make the install directories sticky:
1409 chmod ug=rwx,o=rxt `cat /tmp/installdirs`
1411 If you installed pre-chroot as a normal user there are now some directories
1412 owned by that user even though they should be owned by a package user. This
1413 happens because the package users in question did not/could not recreate
1414 these directories. The following script will fix that. It works by looking
1415 for directories not owned by a registered user (-nouser option). The list
1416 of these directories is processed in a for loop. For each directory find is
1417 then used to look for all files owned by a registered user and to list those
1418 users. With "sort -u" (unique) we get a list of registered users that have
1419 files in the directory in question and wc tells us how many of these users
1420 there are. If there is only one such user (i.e. all files in the directory
1421 belong either to an unregistered user or the one registered user we
1422 identified) then we assign the directory to this user.
1424 ----------------- begin $LFS/usr/src/lfs/fixowner -------------------------
1426 #find all directories owned by an unknown user (i.e. the normal user
1427 #account you installed pre-chroot with, if you installed as a normal
1428 #user) and assign those that have an unambiguous intended owner (i.e.
1429 #only files belonging to a single owner aside from the unknown one
1430 #are found in the directory) to that owner.
1433 owners=`find $1 -not -nouser -printf "%u\n" | sort -u `
1434 num=`echo $owners | wc -w | tr -d " "`
1437 for dir in `find / -path "/proc" -prune -or -type d -nouser -print`
1440 if [ $num == 1 ]; then
1441 chown `echo $owners | tr -d " "`. $dir
1442 chmod u=rwx,go=rx $dir
1443 echo "Assigning $dir to $owners"
1446 ----------------- end $LFS/usr/src/lfs/fixowner ---------------------------
1449 use find to find files that still belong to the user you
1450 installed pre-chroot with. You can also use find to identify any directories
1451 that are group-writeable and not sticky. You should also check for setuid
1452 and setgid programs that don't belong to root. Apply your common sense to
1453 deal with these files (or send me a mail so I can mention the issue here).
1455 find / -path "/proc/*" -prune -or -perm +u+s -printf "%p: suid %u\n"
1456 find / -path "/proc/*" -prune -or -perm +g+s -printf "%p: sgid %g\n"
1457 find / -path "/proc/*" -prune -or \
1458 -type d -perm +o+w -not -perm +o+t -printf "%p: world-writeable\n"
1459 find / -path "/proc/*" -prune -or -type d -perm +g+w -not -perm +o+w \
1460 -not -perm +o+t -printf "%p: group-writeable\n"
1461 find / -path "/proc/*" -prune -or \
1462 -not -type d -not -type l -perm +o+w -printf "%p: world-writeable\n"
1463 find / -path "/proc/*" -prune -or -path "/dev/*" -prune -or \
1464 -not -type d -not -type l -perm +g+w -not -perm +o+w \
1465 -printf "%p: group-writeable\n"
1466 find / -path "/proc/*" -prune -or -nouser -printf "%p: unknown user: %u\n"
1467 find / -path "/proc/*" -prune -or -nogroup -printf "%p: unknown group: %g\n"
1471 ###########################################################################
1472 Security issues with NFS
1473 ###########################################################################
1475 If you use the network filesystem NFS, there are some things you need to
1476 look out for when using the package user system described in this hint.
1477 A fundamental security problem with NFS is that it blindly trusts the uid and
1478 gid of the client. If an attacker can get access to the root account on a
1479 system in your network that is allowed to mount NFS shares from your server,
1480 or if the attacker can attach his own computer to your network, then this
1481 attacker can pretend to be anyone. NFS will happily allow the attacker to
1482 work in the NFS exported directory as any user he wants to be.
1483 The only exception is the root account. By default NFS exports directories
1484 with the root_squash option that maps all incoming requests from uid 0 to
1485 anonuid (65534 unless set in /etc/exports) and gid 0 to anongid
1486 (65534 unless set in /etc/exports). This protects files owned by
1487 root.root. On a normal system this includes most files in /bin, /etc, /lib
1488 and most other directories except /home. If you use the package user scheme,
1489 however, most of these files are owned by package users. These files are
1490 not protected by the root_squash option.
1491 In order to make NFS exports secure, you have to add the option "all_squash"
1492 to every entry in /etc/exports that exports a directory that contains
1493 files owned by package users. Note that all_squash is always a good idea
1494 because even systems that don't use package users usually have some programs
1495 owned by other users such as daemon or bin and other groups such as tty,
1496 because they need to be setuid or setgid.