LinuxSir.cn,穿越时空的Linuxsir!

 找回密码
 注册
搜索
热搜: shell linux mysql
查看: 3854|回复: 18

用 package user(软件包用户)来获得更多的控制和进行软件包管理(v0.8)

[复制链接]
发表于 2003-5-2 11:25:15 | 显示全部楼层 |阅读模式
标题:                用 package user(软件包用户)来获得更多的控制和进行软件包管理(v0.8)
LFS 版本:        3.1 (对以后的版本应该也适用,不过可能要作些小改动)
作者:                Matthias S. Benkmann <m.s.b@gmx.net>

概要:
        -你想知道系统里的文件属于哪个软件包么?
        -你想删除那些不能用 make uninstall的软件么?
        -你对系统里存在的那些 setuid root 的程序感到不安么?
        -你不想让某个软件包覆盖其他软件包安装过的文件吧?
        -你不喜欢 RPM 那样的软件包管理器吧?
        -你想只使用 UNIX 内建的功能就获得完全的控制吧?

正文:

法律声明: The author does not take responsibility for any damage of any kind
caused directly or indirectly by applying the methods described below.
There is no warranty of any kind, not even the implied warranty of fitness for
the purposes mentioned in the text, on any of the programs/scripts/commands
listed below and the author may not be held liable for any damage of any kind
caused by the execution of said programs/scripts/commands.
Proceed at your own risk!

###########################################################################
变动说明
###########################################################################

2002-01-30  -added Changelog
            -moved "chown 0.10000 `cat /tmp/installdirs`" command up (before
             glibc package user is created)
            -add_package_user: create home directory with "mkdir -p"
                               use $grpfile everywhere instead of /etc/group
            -improved mammoth sentence in Introduction
            -added note about possibility to have user name==group name
            -source bashrc_basic in bashrc_package
            -minor textual changes

2002-02-18
            -added section "Security issues with NFS"
            -submitted v0.7
            
2002-03-16  -added note, that on Linux make doesn't need to be setgid kmem

2002-04-20
            -changed LFS VERSION header to be more conservative
            -added <br> tags to the synopsis for the sake of the hints
             index
            -added group mmedia to the list of suggested groups
            -submitted v0.8
            


###########################################################################
介绍
###########################################################################

假设我写了一个程序,正好你需要它。为了你的方便,我过来帮你安装。你会把 root 帐号给我,然后就离开房间么?不会?那你为什么会把它给一个一辈子没见过的陌生人呢?(指安装从 Internet上下载的没有任何保证甚至连内容都没有在 README 里说清楚的软件包,结果搞得系统里到处都是乱七八糟的文件。)真奇怪,Unix的管理员对于他们的雇主甚至都不会给超过普通用户权限的帐号,却会把 root 的权限给那些复杂得难以理解的安装脚本。

用户和组的概念是 Unix 系统中最基本的安全原则。多年来,人们成功地运行它们来控制谁创建了某个文件,哪些人允许修改或删除这个文件。但这种控制只是用在普通用户的文件上,多浪费啊!我建议把它用在系统里所有的文件上。


#############################################################################
Package users
#############################################################################

这种策略的基本思想很容易解释。每一个软件包属于某个确定的"package user"(软件包用户)。这是一个特殊的用户,HOME 目录是 /usr/src/<软件包> (或任何你存放源码压缩包的地方).
通常这个用户没有密码,所以只有 root 能用 su 命令切换到某个 package
user,这就保证了你不会在系统里留下安全隐患。
当然你*可以* 设置密码,以允许某个协助你的管理员安装和维护这个软件包,而不需要真正的 root 权限。比如,这个管理员可以安装,删除,为他的工作组改变某些库文件。然而,他不能删除或修改不属于他的库文件,例如 libc.

每个 package user 都属于一个反映软件包用途的组,如最基本的软件包属于 "system"组,开发工具属于"devel"组等等。当然还有一些有用的组织策略。你可能想让组反映出上面提到的协同管理员。或者为每一个软件包都创建唯一的组,名称与用户名称相同,这样即使有些程序需要 setuid root (因此它会属于 root 用户),这些程序的组也能告诉你它属于哪个软件包.

除了上面的那个组(我们称为首要组)以外,所有的 package user 还属于同一个次要组,叫做"install"组。所有允许安装东东的目录都属于 install 组。这包括 /bin 和 /usr/bin 这样的目录,但不包括 /root 或 /. 属于 install 组的目录是同组可写的。如果没有额外的措施,那还不能保证安全和控制,因为每个软件包都能覆盖另一个软件包的文件(尽管这种变化可以用ls -la命令观察到)。因此,我们把所有install组的目录都加上sticky位。sticky 属性允许用户在目录里创建新文件,删除或修改属于自己的文件,但不允许用户写或删除其他用户的文件。


############################################################################
如何安装软件包
############################################################################

上面说的太单调乏味了?如果你用这个脚本就不会了:
  1. -------------------- begin $LFS/sbin/install_package ------------------------
  2. #!/bin/sh
  3. if [ $# -ne 3 ]; then
  4. echo USAGE: install_package description name group
  5. echo "!!Don't forget to put description in quotes if it contains spaces.!!"
  6. echo "This will create a new package user name in group. This will set up"
  7. echo "a home directory for this package user. After that, this script will"
  8. echo "automatically su to the new user so that you can begin with the"
  9. echo "installation right away. "
  10. exit 1
  11. fi

  12. if [ $UID -ne 0 ]; then echo Please run this script as root. ; exit 1; fi
  13. add_package_user "${1}" $2 10000 20000 $3 10000 20000 || exit 1
  14. su $2
  15. ------------------- end $LFS/sbin/install_package ----------------------------
复制代码


上面给出的脚本是下面脚本的快捷方式,而下面的这个脚本才是执行具体任务,在特定的 UID 和 GID 范围内添加 package user:
 楼主| 发表于 2003-5-2 11:26:06 | 显示全部楼层
  1. -------------------- begin $LFS/sbin/add_package_user -----------------------
  2. #!/bin/sh

  3. if [ $# -lt 7 ]; then
  4. echo 'Copyright (c) 2000 Matthias Benkmann'
  5. echo 'USAGE: '
  6. echo 'add_package_user description name minuid maxuid group mingid maxgid [-d home]'
  7. echo
  8. echo Don\'t forget to put description in quotes.
  9. echo description must be valid for a /etc/passwd entry, that means especially
  10. echo that it must not contain ":".
  11. echo If group doesn\'t exist, it will be added.
  12. echo group will be the user\'s main group.
  13. echo 'The user will also belong to group "install" (created if necessary).'
  14. echo 'A home directory /usr/src/name (or home if -d home specified)
  15. echo will be created if it doesn\'t exist
  16. echo 'and the files from /etc/skel-package will be copied into it (existing'
  17. echo 'target files will *not* be overwritten)'
  18. echo 'minuid (incl.) and maxuid (excl.)  determine'
  19. echo 'the range of UIDs used by this script. The script will pick the first'
  20. echo 'UID greater than all UIDs from this range already in use.'
  21. echo 'If that UID is out of range (i.e. equal to maxuid), the'
  22. echo 'script will pick the first available UID in this range. If the range is'
  23. echo 'full, the script will give up. This process ensures that UIDs for packages'
  24. echo that have been removed, don\'t get reassigned unless really necessary.
  25. echo 'This avoids trouble if some files were overlooked when removing the package.'
  26. echo 'Otherwise these files might be assigned to the wrong package.'
  27. echo 'mingid (incl.) and maxgid (excl.) define'
  28. echo 'the permissible range for the GID of group if it has to be added (and '
  29. echo 'the install group if it must be added).'
  30. exit 1
  31. fi

  32. grpfile=/etc/group
  33. passwd=/etc/passwd
  34. homebase=/usr/src
  35. skel=/etc/skel-package
  36. description=$1
  37. name=$2
  38. minuid=$3
  39. maxuid=$4
  40. gname=$5
  41. mingid=$6
  42. maxgid=$7
  43. home=$homebase/$name

  44. set -- "$@" _eNd_OF_lisT_
  45. while [ "$1" != "_eNd_OF_lisT_" ]; do
  46.   case "$1" in
  47.     -d) shift 1
  48.         if [ "$1" = "_eNd_OF_lisT_" ]; then
  49.           echo 1>&2 "-d directory name missing!"
  50.           exit 1
  51.         fi
  52.         home="$1"
  53.         shift 1
  54.         ;;
  55.     *) temp="$1"
  56.        shift 1
  57.        set -- "$@" "$temp"
  58.        ;;
  59.   esac     
  60. done
  61. shift 1 #throw away _eNd_OF_lisT_

  62. if [ $UID -ne 0 ]; then echo Please run this script as root. ; exit 1; fi

  63. #test if user already exists
  64. grep "^$name:.*" $passwd
  65. if [ $? -eq 0 ]; then
  66.   echo 'Package user does already exist! Do su '$name' to do maintenance work.'
  67.   exit 1
  68. fi

  69. #test if minuid, maxuid, mingid and maxgid are integers, otherwise set
  70. #to defaults.
  71. error=0
  72. expr ${minuid} + 1 2>/dev/null 1>&2 || error=1
  73. expr ${maxuid} + 1 2>/dev/null 1>&2 || error=1
  74. expr ${mingid} + 1 2>/dev/null 1>&2 || error=1
  75. expr ${maxgid} + 1 2>/dev/null 1>&2 || error=1

  76. if [ $error -eq 1 ]; then
  77.   echo Error: Illegal numeric value!
  78.   exit 1
  79. fi

  80. if [ $minuid -ge $maxuid ]; then
  81.   echo 'Error: minuid must be less than maxuid !'
  82.   exit 1
  83. fi

  84. if [ $mingid -ge $maxgid ]; then
  85.   echo 'Error: mingid must be less than maxgid !'
  86.   exit 1
  87. fi


  88. uidlist=`cut -d : -f 3 $passwd | sort -n`

  89. #find last used UID within range
  90. u=0
  91. for i in $uidlist
  92. do
  93.   if [ $i -ge $maxuid ]; then break; fi
  94.   if [ $i -ge $minuid ]; then u=$i; fi
  95. done

  96. #if no UID from the range is used, pick the first, otherwise pick the one
  97. #immediately following the last UID in use.
  98. if [ $u -eq 0 ]; then u=$minuid; else u=`expr $u + 1`; fi

  99. #if the last UID used from the range is maxuid-1, i.e. we may
  100. #not use its successor as UID, then we look for the first unused uid
  101. #in the range.
  102. if [ $u -ge $maxuid ]; then
  103.   u=$minuid
  104.   for i in $uidlist
  105.   do
  106.     if [ $u -eq $i ]; then u=`expr $u + 1` ; fi
  107.     if [ $i -ge $maxuid ]; then break; fi
  108.   done  

  109.   if [ $u -ge $maxuid ]; then
  110.     echo Error: UID range is full!
  111.     exit 1
  112.   fi
  113. fi

  114. echo Will create user $name with uid: $u

  115. unset uidlist

  116. #############################################################################
  117. #                                 group
  118. #############################################################################

  119. #execute the following for gname and "install" to get gids for those 2 groups

  120. g=0
  121. creategroup=0
  122. for group in install $gname
  123. do
  124.   oldg=$g #save gid from previous run
  125.   createinstall=$creategroup
  126.   creategroup=0

  127.   #test if group already exists and extract gid if so
  128.   g=`grep ^${group}:.\* $grpfile | cut -d : -f 3 -`

  129.   #if group does not exist, then check range for a free gid
  130.   if [ z$g = z ]; then
  131.     creategroup=1
  132.    
  133.     gidlist=`cut -d : -f 3 $grpfile | sort -n`

  134.     #find last used GID within range
  135.     g=0
  136.     for i in $gidlist
  137.     do
  138.       if [ $i -ge $maxgid ]; then break; fi
  139.       if [ $i -ge $mingid ]; then g=$i; fi
  140.     done

  141.     #if no GID from the range is used, pick the first, otherwise pick the one
  142.     #immediately following the last GID in use.
  143.     if [ $g -eq 0 ]; then g=$mingid; else g=`expr $g + 1`; fi
  144.    
  145.     #don't reuse gid from previous run
  146.     if [ $g -eq $oldg ]; then g=`expr $g + 1`; fi

  147.     #if the last GID used from the range is maxgid-1, i.e. we may
  148.     #not use its successor as GID, then we look for the first unused gid
  149.     #in the range.
  150.     if [ $g -ge $maxgid ]; then
  151.       g=$mingid
  152.       for i in $gidlist
  153.       do
  154.         if [ $g -eq $i ]; then g=`expr $g + 1` ; fi
  155.         if [ $g -eq $oldg ]; then g=`expr $g + 1` ; fi
  156.         if [ $i -ge $maxgid ]; then break; fi
  157.       done  

  158.       if [ $g -ge $maxgid ]; then
  159.         echo Error: GID range is full!
  160.         exit 1
  161.       fi
  162.     fi
  163.   fi
  164. done

  165. unset gidlist

  166. if [ $createinstall -eq 1 ]; then
  167.   echo Creating group install with gid $oldg
  168.   groupadd -g $oldg install || exit 1
  169. else
  170.   echo Group install has gid $oldg
  171. fi
  172. if [ $creategroup -eq 1 ]; then
  173.   echo Creating group $gname with gid $g
  174.   groupadd -g $g $gname || exit 1
  175. else
  176.   echo Group $gname has gid $g
  177. fi



  178. useradd -c "${description}" -d ${home} -g ${gname} -G install \
  179.         -s /bin/bash -u ${u} ${name}  || exit 1

  180. mkdir -p $home || exit 1

  181. yes n|cp -ai -R ${skel}/{[^.],.[^.],..?}* ${home} 2>/dev/null >/dev/null

  182. cd ${home}
  183. chown --recursive ${u}.${g} .


  184. exit 0
  185. -------------------- end $LFS/sbin/add_package_user -----------------------
复制代码
 楼主| 发表于 2003-5-2 11:26:46 | 显示全部楼层
如果你用上面的两个脚本,安装软件包时可以这样:

install_package 'Foo description' foo foogroup

就会创建 package user:foo,并自动地用 su 命令切换到这个用户,进入它的 HOME 目录里。接下来把源码压缩包拷贝到这个目录下,解压并安装,与以 root 用户安装没什么区别了。


############################################################################
陷阱和解决方法
############################################################################

在使用这个系统时有一些陷阱,常见的如下:

1. 某个软件包的安装脚本写得不好,执着地改变系统目录或它自身文件的属主或权限。
   
2. 某个软件包的安装脚本有合理的原因,要改变它安装的文件的属主或权限。
   
3. 软件包 A 包含的某个程序已经被软件包 B 安装过了。
   
4. 某个软件包需要测试安装它的是不是 root 用户,如果不是,就不安装某些组件。
   
5. 某个软件包创建了一个目录,而其它的软件包要往里面写东西。

1-3 在 make install 阶段通常会造成"Operation not permitted"的错误。注意,这并不是 package user 策略的缺陷,正相反,这是一个有用的特性。正是为了这个,我们才把安装目录设置成 sticky 并使用 package user.我们不希望这种事情在没有自己(神:-)允许的情况下发生。修正这几个问题通常很简单:

1. 观察"make install"的输出。在"Operation not permitted"的前一行通常有肇事的命令,如"install --owner root foo /bin"。用 grep 查找这个软件包的 Makefiles,把肇事的参数(这里是"--owner foo")删掉。有时你可能不想删掉它,那就需要改成某种无害的东西。比如"install -m 4755 ..."想设置 setuid 位,就需要改成"install -m 755 ...".
你可以用一个sed脚本或包装脚本(后面会说),来自动进行这样的改动,省得手动的改 Makefiles.注意,你可以在 configure 阶段后改,也可以修改 Makefiles 的原型 (通常是 Makefile.in 或 Makefile.am).

2. 这种情况基本与 1 相同,但这时软件包有合理的要求(你会接受的)。这种情况很少见,但的确有些软件包要安装 setuid root 的程序,并且这些程序需要 setuid root 才能使用,比如 ping. 这时,就要像 1 一样安装它,在安装了以后再用 root 来改变程序的属性,达到它的运行要求,如:chown root.root /bin/ping && chmod 4755 /bin/ping.
   
3. 这时你就要决定,你需要的到底是哪个软件包里的程序。如果你要替换掉软件包 B 的程序,手动把它删掉就行了。如果你保留想软件包 B 里面的程序,用 grep 在 Makefiles 中查找这个出问题的程序(以及它的 man 手册页). 通常你能找到像这样的一行:
PROGRAMS=foo bar fubar barfu
把要删除的那个程序从上面的列表里去掉就行了。在极为少见的情况下,你可能需要从 Makefiles 中删除一些"install <programname> <destination>"这样的行。
   
4. 这种情况通常在软件包的 INSTALL 文档里会说到,并会为剩下的那些组件留出一个编译目标,比如"install-root". 所以通常就没有什么问题了。

5. 这种情况好办,把那个出问题的目录属主改成 install 组,权限设置成同组可写,并设置 sticky 位。要做这些并不需要 root 的权限,创建这个目录的用户就能改变它的属性。所以你可以在 Makefile 里加上合适的命令。

手动修改 Makefiles 很麻烦,虽然只是偶而才发生。sed 脚本能帮忙,但它们必须根据每个软件包的不同情况而改动。有一个更好的解决方法。在用 package user 策略来编译 LFS 时,我注意到虽然在 Makefile 中有很多可能的命令会导致"Operation not permitted"错误,但它们中间只有很少的几个用到了。到目前为止,我只遇到了与 mkdir, chgrp,
chown 和 install 命令相关的问题。
我写了下列的包装脚本来自动处理出错问题。如果你能确定,这些脚本所在的目录(我的在/usr/src/lfs)在每个 package user 的 PATH 变量里是第一个,你就能安装绝大多数的 LFS 软件包,而无须手动修改 Makefiles.

注意! 这些包装脚本允许"make install"完成,而不会由于"Operation not permitted"错误而中断。然而,禁止掉的命令中有些是正当的(看上面第 2 点).
所以,包装脚本把禁止掉的命令那一行以"***"开头显示在标准错误 stderr 上。
你必须检查这些行,如果有的是合理的操作,你就要手动执行,否则出问题的程序就不能正常运行了。下面是你应该找的东西,以及应该采取的行动:

1. "*** install -m 4xxx -o root" : 这个命令把一个程序设置成 setuid root 的。这里重要的是"-m 4x".很多安装脚本试图使用"install -m 755 -o root"或类似的东西,这可以忽略掉。属主(用"-o owner"指明) 只对 权限为 4xxx(用"-m 4xxx")的程序才重要。如果你确定那个程序真的需要 setuid root(对于 LFS 基础软件包通常是这样),就必须:
       chown root.root <binaryname>
       chmod u+s <binaryname>

2. "*** chgrp xxx" : 这个命令改变程序的组。通常情况下,只有对于 setgid 的程序才需要这样做。你用"ls -l"命令可以看到
rwxr-sr-x   1 util-lin tty          8348 Dec  3  2000 write
这里的"s"(在group那一位上)就表示这个程序是 setgid 的。
目前,下面的包装脚本chgrp只拦截 xxx=tty 中的 chgrp 操作,因为这是安装 LFS 系统时能遇到的唯一例子。如果这样的命令被拦截了,并且你需要让那个程序 setgid,就这样:
           chgrp xxx <binaryname>
           chmod g+s <binaryname>

技巧:
    使用 package user 策略来安装软件包的时候,很重要的一点就是要认真的分析出错日志,尤其是使用下面的包装脚本时。
   
    为了简化这一过程,用下面的命令安装软件
   
      make install 3>&1 1>&2 2>&3 | tee install.err
      
    这个命令把平常见到的编译信息输出(比如错误和正常的信息)到屏幕,还把所有的错误输出到install.err文件里。在安装了以后,你就可以用"cat install.err"来检查错误信息。
    注意"make install | tee install.err" 不能工作,因为它只是拷贝所有的正常信息,而不包括出错信息。
   
    如果你要重定向屏幕信息(比如保存到install log文件里,包含错误和非错误信息)到一个文件,就这样:

      { make install 3>&1 1>&2 2>&3 | tee install.err ;} &>install.log

    你可能需要创建一个 shell 函数,免得每次打这么多命令。


下面是包装脚本:

  1. ------------- begin $LFS/usr/src/lfs/wrappers/mkdir --------------------------
  2. #!/bin/sh

  3. DAISY_CHAIN=""

  4. for p in $(type -ap mkdir) ; do
  5.   if [ ! $p -ef $0 ]; then DAISY_CHAIN=$p ; break ; fi
  6. done

  7. if [ ! -n "$DAISY_CHAIN" ]; then
  8.   echo Cannot find real ${0##*/} command
  9.   exit 1
  10. fi

  11. if [ $UID == 0 ]; then
  12.   exec $DAISY_CHAIN "$@"
  13. fi

  14. watchdir=/usr/share/locale

  15. cmdline="$@"

  16. dirs=""
  17. for((i=$#; $i>0;))
  18. do
  19.   a="$1"
  20.   shift 1; i=$(($i-1))
  21.   case "$a" in
  22.     $watchdir/*) dirs="$dirs ""`expr $a : "$watchdir/\(.*\)"`"
  23.                  set -- "$@" "$a"
  24.                  ;;
  25.     *) set -- "$@" "$a" ;;
  26.   esac
  27. done

  28. $DAISY_CHAIN "$@" || exit $?

  29. test z"$dirs" != z &&
  30. echo 1>&2 '***' mkdir "$cmdline"
  31. for dir in $dirs ; do
  32.   cumuldir=""
  33.   for d in `echo $dirs | sed 's#/# #g' -` ; do
  34.     cumuldir=$cumuldir$d/
  35.     chgrp install $watchdir/$cumuldir
  36.     chmod g+w,o+t $watchdir/$cumuldir
  37.   done  
  38. done
  39. exit 0
  40. ------------ end $LFS/usr/src/lfs/wrappers/mkdir --------------------------
复制代码

  1. ------------ begin $LFS/usr/src/lfs/wrappers/chgrp ------------------------
  2. #!/bin/sh

  3. DAISY_CHAIN=""

  4. for p in $(type -ap chgrp) ; do
  5.   if [ ! $p -ef $0 ]; then DAISY_CHAIN=$p ; break ; fi
  6. done

  7. if [ ! -n "$DAISY_CHAIN" ]; then
  8.   echo Cannot find real ${0##*/} command
  9.   exit 1
  10. fi

  11. if [ $UID == 0 ]; then
  12.   exec $DAISY_CHAIN "$@"
  13. fi

  14. if [ "$1" == "tty" ]; then
  15.   echo 1>&2 '***' chgrp "$@"  
  16. else
  17.   $DAISY_CHAIN "$@" || exit $?
  18. fi

  19. exit 0
  20. ------------ end $LFS/usr/src/lfs/wrappers/chgrp --------------------------
复制代码

  1. ------------ begin $LFS/usr/src/lfs/wrappers/chown ------------------------
  2. #!/bin/sh

  3. DAISY_CHAIN=""

  4. for p in $(type -ap chown) ; do
  5.   if [ ! $p -ef $0 ]; then DAISY_CHAIN=$p ; break ; fi
  6. done

  7. if [ ! -n "$DAISY_CHAIN" ]; then
  8.   echo Cannot find real ${0##*/} command
  9.   exit 1
  10. fi

  11. if [ $UID == 0 ]; then
  12.   exec $DAISY_CHAIN "$@"
  13. fi

  14. if [ "$1" == "root.root" ]; then
  15.   echo 1>&2 '***' chown "$@"  
  16. else
  17.   $DAISY_CHAIN "$@" || exit $?
  18. fi

  19. exit 0
  20. -------------- end $LFS/usr/src/lfs/wrappers/chown ------------------------
复制代码

  1. ------------ begin $LFS/usr/src/lfs/wrappers/install ----------------------
  2. #!/bin/sh

  3. localedir=/usr/share/locale
  4. cmdline="$@"
  5. manpagesowner=man-pages

  6. DAISY_CHAIN=""

  7. for p in $(type -ap install) ; do
  8.   if [ ! $p -ef $0 ]; then DAISY_CHAIN=$p ; break ; fi
  9. done

  10. if [ ! -n "$DAISY_CHAIN" ]; then
  11.   echo Cannot find real ${0##*/} command
  12.   exit 1
  13. fi

  14. if [ $UID == 0 ]; then
  15.   exec $DAISY_CHAIN "$@"
  16. fi


  17.         #********** test if we create directories ********************
  18. if [ \( z"$1" = z"-d" \) -o \( z"$1" = z"-m" -a z"$3" = z"-d" \) ]; then  
  19.   locdirs=""
  20.   notify=0
  21.   havedir=0
  22.   for((i=$#; $i>0; ))
  23.   do
  24.     a="$1"
  25.     shift 1; i=$(($i-1))
  26.     case "$a" in
  27.       -o|-g|--owner|--group) notify=1
  28.                               shift 1; i=$(($i-1))
  29.                           set -- "$@"
  30.           ;;
  31.       $localedir/*) if [ ! -d "$a" ]; then
  32.                             locdirs="$locdirs ""`expr $a : "$localedir/\(.*\)"`"
  33.                       set -- "$@" "$a"
  34.                       havedir=1
  35.                     else
  36.                       notify=1
  37.                       set -- "$@"
  38.                     fi  
  39.                    ;;
  40.       */*|/sbin) if [ ! -d "$a" ]; then
  41.                    set -- "$@" "$a"
  42.                    havedir=1
  43.                  else
  44.                    notify=1
  45.                    set -- "$@"
  46.                  fi            
  47.                  ;;
  48.       *) set -- "$@" "$a" ;;
  49.     esac
  50.   done
  51.   
  52.   test $notify -eq 1 -o z"$locdirs" != z && \
  53.                                           echo 1>&2 '***' install "$cmdline"

  54.   test $havedir -eq 0 && exit 0

  55.   $DAISY_CHAIN "$@" || exit $?
  56.   
  57.   test z"$locdirs" != z &&
  58.   for dir in $locdirs ; do
  59.     cumuldir=""
  60.     for d in `echo $locdirs | sed 's#/# #g' -` ; do
  61.       cumuldir=$cumuldir$d/
  62.       if [ -d $localedir/$cumuldir ]; then
  63.         chgrp install $localedir/$cumuldir
  64.         chmod g+w,o+t $localedir/$cumuldir
  65.       fi  
  66.     done  
  67.   done

  68. else  #if "$1" != "-d"  ,i.e. we do not create directories *****************
  69.   notify=0
  70.   for((i=$# ; $i>0; ))
  71.   do
  72.     a="$1"
  73.     shift 1; i=$(($i-1))
  74.     case "$a" in
  75.      -m)      set -- "$@" "$a"
  76.               a="$1"
  77.               shift 1; i=$(($i-1))
  78.               case "$a" in
  79.                 4755) notify=1 ; set -- "$@" "755" ;;
  80.                 *) set -- "$@" "$a"  ;;
  81.               esac
  82.           ;;
  83.       -m4755) notify=1 ; set -- "$@" "-m755" ;;
  84.       -o|-g|--owner|--group)    notify=1
  85.                       shift 1; i=$(($i-1))
  86.                   set -- "$@"
  87.           ;;
  88.       */man/man?/*)
  89.                 if [ -e "$a" -a ! -O "$a" ]; then
  90.                   if [ `find "$a" -printf \%u` = $manpagesowner ]; then
  91.                     notify=1
  92.                     set -- "$@" not_installed
  93.                   else
  94.                     set -- "$@" "$a"
  95.                   fi  
  96.                 else
  97.                   set -- "$@" "$a"
  98.                 fi
  99.           ;;   
  100.       *) set -- "$@" "$a" ;;
  101.     esac
  102.   done

  103.   test $notify -eq 1 && echo 1>&2 '***' install "$cmdline"

  104.   $DAISY_CHAIN "$@" || exit $?
  105. fi

  106. exit 0
  107. ----------- end $LFS/usr/src/lfs/wrappers/install --------------------------
复制代码

如果你改进了上面的脚本,能寄给我一份就太好了,最好说明一下是哪个包提示你作出这样的改进。注意这些脚本尽量作出最少的改动,所以输入一个"-m*)"而不是"-m4755"就不行了。
这些脚本只能处理(我)已经遇到过的问题,而不是所有可能的问题。
 楼主| 发表于 2003-5-2 11:27:12 | 显示全部楼层
#############################################################################
对于 LFS 的专门指导
#############################################################################

对于 package user 的用户名,我通常使用软件包的名称,不包含版本号,但包含横线,名字可以长于8个字符,比如"util-linux".
除了 ls -l 会去掉用户名中最后的字符以外,我还没有碰到什么问题。我测试过的其他程序都能正确处理这样的用户名。如果你遇到了问题,请告诉我。

那么,我们应该怎样在编译 LFS 系统的时候用上面的方法呢?

##########################################################################
chroot 以前的阶段
##########################################################################

在 chroot 前我们不使用 package users.但这并不意味着我们要用 root 来安装。我建议用普通用户来安装 chroot 前的系统。这样,在最后你就能分辨在 chroot 环境中创建的文件 (它们的 uid 是 0 或 某个 package user 的 uid) 和剩下来的在chroot前创建的文件(它们带着你主系统里的正常用户id). 作为普通用户编译还能保证不会因为失误而覆盖主系统里的文件。

在 chroot 前的命令不需要改变,但你需要加一些东西。第一个问题是我们在 chroot 中安装了 glibc 后马上就需要 find。最好的办法是在 chroot 前编译一个静态的 find:

./configure --prefix=$LFS/usr --disable-nls &&
make LDFLAGS=-static CPPFLAGS=-Dre_max_failures=re_max_f2 &&
make libexecdir=$LFS/usr/bin install

另一个问题是,在glibc安装前,某些程序比如chown,不能解释用户名称。幸运的是多数程序都接受数字形式的 UID 或 GID. 只有一个例外: su. 使用 package user 的基础竟然不能接受数字形式的 id:-|

那么我们在安装 glibc 前要怎样成为一个 package user 呢?你可能以为我写了一个脚本来代替它,你猜 ...
... 错了 :-) 我写了一个 C 程序来代替它.

碰巧 sh-utils 也包含了一个 su 程序,LFS里面没有用它。在 chroot 前静态编译安装了这个程序,但从来就没有用到(假设是标准的 LFS 安装), 在动态编译 sh-utils 时被替换,最后又被 shadow 中的 su 替换掉,这个才是最后的 LFS 系统里用到的 su 程序。所以你需要做的就是,在第五章编译 sh-utils 前,把 sh-utils 源码树下 src/su.c 用下面的内容替换。
注意如果你用普通用户编译第五章,你需要在"make install"后再运行"make install-root",要不然 sh-utils 就不会安装 su.

  1. -------- begin $LFS/usr/src/sh-utils/sh-utils-<version>/src/su.c ------------
  2. #include <stdio.h>
  3. #include <sys/types.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <errno.h>
  7. #include <string.h>
  8. #include <ctype.h>
  9. #include <grp.h>

  10. #define NUMGIDS 1024

  11. int main(int argc,char* argv[])
  12. {
  13.   char* Res;
  14.   char Buffy[4096];
  15.   uid_t tuid=-1,uid;
  16.   gid_t gid;
  17.   int i;
  18.   FILE* File;
  19.   char* command=NULL;
  20.   char* shell;
  21.   char* HOME;
  22.   gid_t gid_list[NUMGIDS];
  23.   ssize_t NumGids=0;

  24.   if (argc>1)
  25.   {
  26.     if (strcmp(argv[1],"--help")==0)
  27.       { fprintf(stdout,"There is no help!\n"); exit(0);}
  28.     if (strcmp(argv[1],"--version")==0) {fprintf(stdout,"0.6\n"); exit(0); }
  29.   }

  30.   if ((argc==4) && (strcmp(argv[2],"-c")==0)) command=argv[3]; else
  31.   if (argc!=2)
  32.   {
  33.     fprintf(stdout,"USAGE: su username|uid [-c command]\n");
  34.     return 1;
  35.   };

  36.   i=0;
  37.   while(isdigit(argv[1][i])) ++i;
  38.   if (argv[1][i]==0) tuid=atol(argv[1]);

  39.   File=fopen("/etc/passwd","rb");
  40.   if (File==NULL) {perror("/etc/passwd"); return 1;};
  41.   
  42.   while(1)
  43.   {
  44.     errno=0;
  45.     Res=fgets(Buffy,1024,File);
  46.     if (Res==NULL) {
  47.       if (errno!=0) perror("/etc/passwd");
  48.         else fprintf(stderr,"su: User not found!\n");
  49.       return 1;
  50.     };
  51.    
  52.     Res=strtok(Buffy,":");
  53.     if (Res==NULL) continue;
  54.     strtok(NULL,":");
  55.     uid=atol(strtok(NULL,":"));
  56.     gid=atol(strtok(NULL,":"));
  57.     strtok(NULL,":");
  58.     HOME=strtok(NULL,":");
  59.     shell=strtok(NULL,":");
  60.     if (tuid==uid) {argv[1]=strdup(Buffy); break;}
  61.     if (strcmp(argv[1],Buffy)==0) break;
  62.   };
  63.   HOME=strdup(HOME);
  64.   shell=strdup(shell);
  65.   
  66.   File=fopen("/etc/group","rb");
  67.   if (File==NULL) {perror("/etc/group"); return 1;};
  68.   
  69.   while(1)
  70.   {
  71. ContinueReadingEtcGroup:
  72.     errno=0;
  73.     Res=fgets(Buffy,1024,File);
  74.     if (Res==NULL) {
  75.       if (errno!=0) {perror("/etc/group"); return 1;} else break;
  76.     };
  77.    
  78.     Res=strtok(Buffy,":,\n");
  79.     if (Res==NULL) continue;
  80.     strtok(NULL,":,\n");
  81.     gid_list[NumGids]=atol(strtok(NULL,":,\n"));
  82.     Res=strtok(NULL,":,\n");

  83.     while(Res!=NULL)
  84.     {
  85.       if (strcmp(Res,argv[1])==0)
  86.       {
  87.         ++NumGids;
  88.         if (NumGids>=NUMGIDS) goto SetNewIdentity;
  89.                          else goto ContinueReadingEtcGroup;
  90.       };
  91.       Res=strtok(NULL,":,\n");
  92.     };
  93.   };

  94. SetNewIdentity:
  95.   if (command==NULL) command=shell;
  96.   setenv("HOME",HOME,1);
  97.   setgroups(NumGids,gid_list);
  98.   setgid(gid);
  99.   setuid(uid);
  100.   errno=0;
  101.   i=system(command);
  102.   if (((i<0) || (i==127)) && (errno!=0)) {perror("/bin/sh"); return 1;};
  103.   return i;
  104. };
  105. -------- end $LFS/usr/src/sh-utils/sh-utils-<version>/src/su.c ------------
复制代码

这个 su 程序能接受用户名,也能接受数字形式的UID. 它使用 /etc/passwd 来进行独立的名称解释,所以在 glibc 安装前也能工作。
注意 sh-utils 安装 su 程序时会设置成 setuid 的。
如果你用普通用户来做第五章(为了安全,你应该这样), 即使是 root 来执行,su也不能工作。只要去掉 setuid 位就行了:

chmod u-s $LFS/bin/su

我们一进入 chroot 环境(这时是root身份),就能正常的使用 su. 注意你可以从 root 切换到某个 package user,但不能反着来。如果你想从 package user 用 su 切换到 root,就必须设置 su 为 setuid root(比如 chown root.root $LFS/bin/su && chmod u+s $LFS/bin/su). 确保你不会在其他人能接触到的系统里放这样的程序.
上面的 su 不检查密码,所以如果你让它setuid root,任何人都能成为 root,而无需密码!
 楼主| 发表于 2003-5-2 11:27:52 | 显示全部楼层
##########################################################################  
Chroot 阶段 - 准备 LFS 系统:
##########################################################################

这是一个棘手的阶段。在我们刚一进入 chroot 时,我们必须把安装目录分配给 install 组,并让它们是同组可写的。然而,因为 glibc 还没有安装,我们不能使用用户名,更麻烦的是,groupadd 和 useradd 属于 shadow 软件包,我们还没有装。
这就意味着如果不手动编辑 /etc/group 和 /etc/passwd 文件,我们就不能创建 package user.
但别绝望,下面的脚本能在没有安装 shadow 软件包的情况下完美的代替 useradd 和 groupadd.注意它们只能接受 add_package_user 脚本里的那种 useradd 和 groupadd 命令格式。我假定你将使用 add_package_user 脚本来为系统添加 package user.下面是脚本:

  1. --------------------- begin $LFS/usr/sbin/useradd ----------------------------  
  2. #!/bin/sh
  3. 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
  4. echo 1>&2 USAGE: useradd -c description -d home -g maingroup -G addgroup -s shell -u uid login
  5. exit 1
  6. fi

  7. #test if user already exists
  8. grep "^${13}:.*" /etc/passwd
  9. if [ $? -eq 0 ]; then
  10.   echo 1>&2 $0: User does already exist
  11.   exit 1
  12. fi      

  13. g=`grep ^${6}:.\* /etc/group | cut -d : -f 3 -`
  14. if [ z${g} = z ]; then
  15.   echo 1>&2 $0: Group ${6} does not exist!
  16.   exit 1
  17. fi

  18. grep ^${8}:.\* /etc/group >/dev/null || \
  19. {
  20.   echo 1>&2 $0: Group ${8} does not exist!
  21.   exit 1
  22. }


  23. cp /etc/passwd /tmp/passwd123456
  24. echo "${13}:x:${12}:$g:$2:$4:/bin/bash" \
  25. | sort -t : -k3,3n -m /tmp/passwd123456 - > /etc/passwd


  26. cp /etc/group /tmp/group123456
  27. sed  -e 's/^\('"${8}"':[^:]*:[0-9]*:..*\)$/\1,'"${13}"'/' \
  28.      -e 's/^\('"${8}"':[^:]*:[0-9]*\):$/\1:'"${13}"'/' \
  29.                                                      /tmp/group123456 >/etc/group
  30. ---------------------- end $LFS/usr/sbin/useradd ----------------------------  
复制代码

  1. --------------------- begin $LFS/usr/sbin/groupadd --------------------------
  2. #!/bin/sh
  3. if [ $# -ne 3 -o z$1 != z-g ]; then
  4. echo 1>&2 USAGE: groupadd -g gid groupname
  5. exit 1
  6. fi

  7. #test if group already exists
  8. grep "^${3}:.*" /etc/group
  9. if [ $? -eq 0 ]; then
  10.   echo 1>&2 $0: Group does already exist
  11.   exit 1
  12. fi      

  13. cp /etc/group /tmp/group123456
  14. echo ${3}:x:${2}: | sort -t : -k3,3n -m /tmp/group123456 - > /etc/group
  15. --------------------- end $LFS/usr/sbin/groupadd ----------------------------
复制代码

这些脚本克服了缺少 shadow 软件包的问题。

用上面的脚本和 su 程序,我们就能开始 chroot 阶段了。

###########################################################################
进入 chroot 前的技巧
###########################################################################

当 add_package_user 创建一个新的 package user 时,会拷贝所有 /etc/skel-package (如果你创建了这个目录的话)目录下的文件到那个 package user 的 home 目录。 符号链接也会作为符号链接来拷贝。
可以在 /etc/skel-package 下创建一个有用的符号链接:

.bashrc -> /etc/bashrc_package

内容如下:

  1. ------------------ begin $LFS/etc/bashrc_package --------------------------
  2. #first load basic configuration to make system work normal
  3. source /etc/bashrc_basic

  4. export PATH=/usr/src/lfs/wrappers:$PATH

  5. #make prompt reflect that we are a package user. Do it via USER_PROMPT_COMMAND
  6. #rather than PROMPT_COMMAND because we want to keep root's prompt if we su to root
  7. export USER_PROMPT_COMMAND='PS1="package \u:"`pwd`"> "'

  8. alias mafobu='make -f /usr/src/lfs/mafobu'

  9. #The following command will put us in the home directory.
  10. cd
  11. ------------------ end $LFS/etc/bashrc_package ----------------------------
复制代码

  1. ------------------ begin $LFS/etc/bashrc_basic ----------------------------
  2. #This file should be sourced by all users' .bashrc files

  3. if [ $UID -eq 0 ]; then
  4.   export PATH=/usr/local/sbin:/sbin:/bin:/usr/sbin:/usr/bin
  5. else
  6.   export PATH=/usr/local/bin:/bin:/usr/bin
  7. fi

  8.   #set a red prompt when user is root. If a user wants to override this
  9.   #s/he should use USER_PROMPT_COMMAND instead of PROMPT_COMMAND in order to
  10.   #keep
  11.   #root's prompt when su'ed to root. If a user wants to change PS1 directly
  12.   #s/he should do "unset USER_PROMPT_COMMAND".
  13. export PROMPT_COMMAND='if [ $UID -eq 0 ]; then \
  14.   PS1="\[\033[0;31m\]root@\h:"`pwd -P`"# \[\033[0m\]" ; \
  15.     else eval $USER_PROMPT_COMMAND ; fi'
  16.   #make PROMPT_COMMAND read-only to protect against careless users
  17. declare -r PROMPT_COMMAND
  18.   #set a reasonable default USER_PROMPT_COMMAND
  19. export USER_PROMPT_COMMAND='PS1="\u@\h:"`pwd`"> "'

  20.   #make keys (del, bs, home, end,...) work normal
  21. export INPUTRC=/etc/inputrc
  22. bind -f $INPUTRC
  23. ------------------ end $LFS/etc/bashrc_basic ------------------------------
复制代码



另一个可以放在 /etc/skel-package 下的有用文件是 .project,比如:

  1. ------------------ begin $LFS/etc/skel-package/.project ------------------
  2. DESCRIPTION:
  3.   bogus package
  4. CONTENTS:
  5.   foo,bar,fubar
  6. LAST UPDATED:
  7.   30 Feb 2042
  8. DOWNLOAD LOCATION:
  9.   [url]ftp://ftp.gnu.org/gnu/foo/[/url]
  10. WEB SITE:
  11.   <none>
  12. INSTALL NOTES:
  13. GENERAL NOTES:
  14. ----------------- end $LFS/etc/skel-package/.project ------------------
复制代码

每安装或重新安装一个软件包的时候都更新一下这个文件。当你运行"finger <package>" 或 "pinky -l <package>"命令时,就会显示出这个文件的内容。


###########################################################################
在 chroot 环境中
###########################################################################

当你进入 chroot 环境后,运行

groupadd -g 10000 install

会创建一个 install 组。再运行:

chown 0.10000 `cat /tmp/installdirs`
chmod ug=rwx,o=rx `cat /tmp/installdirs`

来给 install 组分配目录。注意,我们还没有设置目录的 sticky 位。这是因为它们还包含了 root 或某个未知用户的文件(如果你在 chroot 前是用普通用户来安装的话). 这些文件必须是可以覆盖的。/tmp/installdirs 文件如下:

  1. ----------------- begin $LFS/tmp/installdirs ------------------------------
  2. /usr/bin
  3. /usr/etc
  4. /usr/sbin
  5. /usr/include
  6. /usr/lib
  7. /usr/man/man?
  8. /usr/doc
  9. /usr/info
  10. /usr/local/man/man?
  11. /usr/local/doc
  12. /usr/share
  13. /usr/share/dict
  14. /usr/share/doc
  15. /usr/share/info
  16. /usr/share/locale
  17. /usr/share/man/man1
  18. /usr/share/man/man2
  19. /usr/share/man/man3
  20. /usr/share/man/man4
  21. /usr/share/man/man5
  22. /usr/share/man/man6
  23. /usr/share/man/man7
  24. /usr/share/man/man8
  25. /usr/share/nls
  26. /usr/share/misc
  27. /usr/share/terminfo
  28. /usr/share/zoneinfo
  29. /usr/share/i18n
  30. /usr/share/aclocal
  31. /usr/local/bin
  32. /usr/local/etc
  33. /usr/local/include
  34. /usr/local/lib
  35. /usr/local/sbin
  36. /usr/local/share
  37. /usr/local/share/dict
  38. /usr/local/share/doc
  39. /usr/local/share/info
  40. /usr/local/share/locale
  41. /usr/local/share/man/man1
  42. /usr/local/share/man/man2
  43. /usr/local/share/man/man3
  44. /usr/local/share/man/man4
  45. /usr/local/share/man/man5
  46. /usr/local/share/man/man6
  47. /usr/local/share/man/man7
  48. /usr/local/share/man/man8
  49. /usr/local/share/nls
  50. /usr/local/share/misc
  51. /usr/local/share/terminfo
  52. /usr/local/share/zoneinfo
  53. /etc
  54. /sbin
  55. /bin
  56. /lib
  57. ----------------- end $LFS/tmp/installdirs --------------------------------
复制代码

现在运行

chown 0.10000 /usr/share/info/dir
chmod ug=rw,o=r /usr/share/info/dir

将使软件包能安装它们的 info 页。



#########################################################################
Chroot 阶段 - 安装软件包
#########################################################################

现在我们能安装 glibc 了。以 root 身份用下列命令创建 /dev/null

mknod -m 0666 /dev/null c 1 3

then do

install_package "GNU C library" glibc system

这会创建 system 组和 glibc 用户,还会用 su 命令切换到 glibc 软件包用户。注意mkdir 和 install 包装脚本在安装 glibc 时不适用,因为它们使用"install"而不是 gid.所以我们在安装完 glibc 后要这样:

find /usr/share/locale/* -type d -user glibc -exec chmod ug=rwx,o=rxt \{\} \;\
                                         -exec chgrp install \{\} \;


现在

exit

以回到 root,并继续安装剩下的软件包。使用命令

install_package <description> <packagename> <group>

来创建和切换到 package user. 然后在用户的目录里解压源码包,并结合下面的说明与 LFS BOOK 里的指导来安装每个软件包。

############################################################################
用户组的指导方针
############################################################################

我推荐对于 package user 使用下面的用户组:

devel: 开发相关的东西,如编译器。并不局限于软件开发,Tex 也可以属于这个组。
      
utils: 多数软件包属于这个组,甚至一些很基础的软件包如 grep 和文本编辑器。
      
net: 网络相关的东西如 ftp 守护进程或网络浏览器。这个组在很大程度上与其他组重叠。当某个软件包主要是用在 Internet, LAN, WWW,... 时,应该优先用这个组。比如像 wget 这样的工具应该属于 net 而不是 utils 组。例外,对于 docs, addons, games 和 mmedia 组,如果有一个软件包属于它们与 net 的交界,优先不用 net 组。
     
docs: 文档相关的软件包,如 Linux howto 的压缩包。注意,创建文档的软件包如 XML 处理器可能应该属于 devel 组,用来看文档和后期处理的程序如 man 和 groff 应该属于 utils 组。
      
system: 重要的系统软件包,如 bash. 这个组只应该用于非常重要的软件包。多数你想放到这里面的程序应该放到"utils"里。比如 vi 就属于 utils.如果某个软件包不属于 LFS 的基本系统,那它就不太可能属于 system 组。
        
libs: 就是库文件啦。与其他类别关系不密切的库文件就应该属于这个组,如 zlib 或 libpng.最基础的系统库文件,如 glibc, ncurses 或 gettext,应该属于 system 组。libs 组也应该包含运行时间库环境(run-time environments)如 Java 虚拟机,dosemu 和 wine. 其他的模拟器如 MAME 应该属于 games 组。
     
games: 你期待的东东;-)

mmedia: 包含音频,视频编辑器,mp3播放器等。

apps: 电子表格或字处理程序(不是文本编辑器)以及 CAD 程序和图像程序如 Gimp 都属于这个组。apps 组有点像 utils,但它包含的程序更用户友好,比 utils 更流程化和具体化。apps 感觉起来不像 utils 那么野和令人讨厌。例如 Emacs 就应该属于 utils.

addons: 插件,过滤器以及类似的东东,特征是它们都需要与其他软件包一起使用。
      
x: 与整个 X Window 系统相关的软件,并且不符合其他组,比如 X 服务器本身以及窗口管理器。多数 X 软件应该放到更具体的组里。如 xmine 这样的游戏应该属于 games 组,有图形界面的文本编辑器应该属于 utils 组。
   
kde: 与 KDES 相关并且不属于任何其他组的软件包。应该谨慎的使用这个组,不要把所有的 KDE 软件包都放在这个组里。比如 K Office 就属于 apps 组。Konqueror 属于 net.
     
gnome: 与 GNOME 相关并且不属于任何其他组的软件包。应该谨慎的使用这个组,不要把所有的 GNOME 软件包都放在这个组里。比如 Gimp 属于 apps. GNOME 兼容的窗口管理器,如果能用在单纯的 X 下,属于 x 组。

下面是我认为比较合适的 LFS 软件包分组列表。通常用户名称应该是源码压缩包去掉后缀名和版本号后的名字。下面的分组尽量符合上面的规则,但有的时候真的很难找出"正确"的组。列表里还包含了我在安装它们时做的一些记录。如果你使用 mkdir, chgrp 和 install 包装脚本,你就应该没什么问题了。

glibc.system:
  见前述。

MAKEDEV: 不要安装成 package user

man-pages.docs
findutils.utils
mawk.devel:
        rm /usr/bin/mawk /usr/share/man/man1/mawk.1
        as root before `make install'
ncurses.system
vim.utils
ed.utils
gcc.devel:
        在运行`make install'前把 /usr/lib/gcc-lib 和 /usr/include/g++ 分配给 gcc package user,命令如下(作为root)
        chown -R gcc. /usr/lib/gcc-lib /usr/include/g++
bison.devel
less.utils
groff.utils
man.utils
perl.utils
m4.devel
texinfo.utils
autoconf.devel
automake.devel:
        创建目录 /usr/share/aclocal. 这个目录是其他软件包可写的,所以安装后要用 chgrp 和 chmod g+w,o+t 来改变属组和权限.

bash.system:
        当以 package user 身份运行 ./configure 时(更确切的说是在 su 环境里时),它会不能正常检测到 /dev/stdin. 这会让 bash 编译成 /dev/std* 和 /dev/fd/* 的模拟(emulation)。我实际上更喜欢这种模拟,因为它符合 man 手册页上的描述,但不能完全正常的工作(如打开各自的设备)。如果你想要一个正常的 bash,就用 sed 来修改 configure 脚本,把 "test -r /dev/stdin" 换成 "test -e /dev/stdin" (对 /dev/fd 也一样).[这是对于 bash 2.05 来说,我已经报告了这个问题,所以在以后的版本里可能已经修正了。]
flex.devel
file.utils
libtool.devel
bin86.devel
binutils.devel
bzip2.utils
gettext.system
kbd.system
diffutils.utils
e2fsprogs.utils
fileutils.system
grep.utils
gzip.utils
lilo.system
make.devel:
        make 想设置成 setgie kmem,以使 -l 参数可用。如果你不是知道这个参数作用的开发者,就不需要它,不用为 make 设置这个特殊权限。
        [更新] 在 Linux 系统上,即使不 setgid kmem,-l 参数也能工作。

linux kernel: 不作为 package user 安装
modutils.system:
        modutils 在使用模块前会检测模块是否属于 root.这就是用 root 而不是 package user 来编译和安装内核的原因。

netkit-base.net
patch.utils
procinfo.utils
procps.utils
psmisc.utils
sed.utils
sh-utils.system
net-tools.net
shadow.system:
        这个软件包会安装比较多的 setuid 程序。如果你对 package user 不是宗教一样的信仰,可能应该用 root.root 来安装,这样简单些。如果你作为 package user 来安装,在安装后别忘了手动把出问题的程序设置成 setuid root 的。在安装了 shadow 后,确定一下 useradd 和 groupadd 脚本已经被正确的替换了。运行
        find / -maxdepth 3 -name groupadd
        来确定你没有两个程序(比如一个在 /sbin 目录下而另一个在 /usr/sbin 目录下)  
       

sysklogd.system
        这个软件包里的 /usr/bin/install 是硬编码的,所以用
          make INSTALL=install install
        来安装,可以保证它使用包装脚本.

sysvinit.system:
        这个包最棘手。在执行"make install"前,你必须作为 root 手动创建 /dev/initctl,因为一个 package user 没有 /dev 目录下的写权限。作为 root 运行下列命令:
          rm -f /dev/initctl
          mknod -m 600 /dev/initctl p

tar.utils
textutils.utils
util-linux.system:
        在安装了这个软件包后别忘了手动把 mount 和 umount 设置成 setuid root 的。write 程序需要设置成 setgid tty 的。

###########################################################################
最后的步骤
###########################################################################

在安装完所有东东后,设置 install 目录为 sticky 的:

chmod ug=rwx,o=rxt `cat /tmp/installdirs`

如果你在进入 chroot 前是用普通用户来安装的,那么现在就会有一些目录的属主是那个用户,而它们本应属于某个 package user. 会这样是因为这些 package user 没有或不能重新创建这些目录。下列脚本能修正这个问题。它查找不属于已注册用户的目录(-nouser 参数)。在一个 for 循环里处理这些目录。对于每一个找到的目录,都查找属于某个已注册用户的文件,并列出这些用户。通过"sort -u" (unique) 我们就能得到已注册用户的列表,wc 告诉我们有多少个这样的用户。如果只有一个这样的用户(比如某个目录下所有文件都要么属于一个没有注册的用户,要么属于我们能认出的一个注册用户)那么就把这个目录分给找出的那个注册用户。.

  1. ----------------- begin $LFS/usr/src/lfs/fixowner -------------------------
  2. #!/bin/sh   
  3. #find all directories owned by an unknown user (i.e. the normal user
  4. #account you installed pre-chroot with, if you installed as a normal
  5. #user) and assign those that have an unambiguous intended owner (i.e.
  6. #only files belonging to a single owner aside from the unknown one
  7. #are found in the directory) to that owner.
  8. get_dir_owners()
  9. {
  10.   owners=`find $1 -not -nouser -printf "%u\n" | sort -u `
  11.   num=`echo $owners | wc -w | tr -d " "`
  12. }                 

  13. for dir in `find / -path "/proc" -prune -or -type d -nouser -print`
  14. do
  15.   get_dir_owners $dir
  16.   if [ $num == 1 ]; then
  17.     chown `echo $owners | tr -d " "`. $dir
  18.     chmod u=rwx,go=rx $dir
  19.     echo "Assigning $dir to $owners"
  20.   fi  
  21. done
  22. ----------------- end $LFS/usr/src/lfs/fixowner ---------------------------
复制代码

最后你需要用 find 来查找那些仍然属于 chroot 前所用用户的文件。你也可以用 find 来查找任何同组可写而又没有设置 sticky 位的目录。你还应该找出那些设置了 setuid
和 setgid 而属主又不是 root 的程序。使用常规方法来处理这些文件(或者给我一封信,我就能在这里提到你所遇到的问题)。

find / -path "/proc/*" -prune -or -perm +u+s -printf "%p: suid %u\n"
find / -path "/proc/*" -prune -or -perm +g+s -printf "%p: sgid %g\n"
find / -path "/proc/*" -prune -or \
       -type d -perm +o+w -not -perm +o+t -printf "%p: world-writeable\n"
find / -path "/proc/*" -prune -or -type d -perm +g+w -not -perm +o+w \
                             -not -perm +o+t -printf "%p: group-writeable\n"
find / -path "/proc/*" -prune -or \
       -not -type d -not -type l -perm +o+w -printf "%p: world-writeable\n"
find / -path "/proc/*" -prune -or -path "/dev/*" -prune -or \
       -not -type d -not -type l -perm +g+w -not -perm +o+w \
                                           -printf "%p: group-writeable\n"
find / -path "/proc/*" -prune -or -nouser -printf "%p: unknown user: %u\n"
find / -path "/proc/*" -prune -or -nogroup -printf "%p: unknown group: %g\n"



###########################################################################
使用 NFS 时的安全问题
###########################################################################

如果你使用网络文件系统 NFS,要用本文描述的 package user 系统,需要注意下面几件事情。NFS 中最基本的安全问题在于它盲目的相信客户端的 uid 和 gid。如果攻击者能取得网络中某个能加载服务器上 NFS 的系统的 root 帐号,或者攻击者把自己的机子连到了你的网络上,那么它就能假装成任何人。NFS 会高兴地允许攻击者在 NFS 共享的目录里以任何用户的身份来工作,除了 root 身份。缺省情况下,NFS 用 root_squash 参数导出目录,把从 uid 0 收到的所有请求映射到 anonuid (如果没有在 /etc/exports 中设置的话就是65534)同时把 gid 0 映射到 anongid(如果没有在 /etc/exports 中设置的话就是65534).这样能保护 root.root拥有的文件。在正常的系统里,这样差不多就包含了所有 /bin, /etc, /lib
以及其他目录下的文件,除了/home. 但是如果你使用 package user 策略,大多数文件都是 package users 拥有的,这些文件不会被 root_squash 参数保护。为了使 NFS 导出更安全,你需要在 /etc/exports 的每一个项目里都加上参数"all_squash",以导出一个包含了 package user 文件的目录。注意 all_squash 总是个好主意,因为即使系统不使用 package user,通常也会有一些程序属主为其他用户,比如守护进程和 bin,还有些程序的属主是其他组,比如 tty,因为它们需要 setuid 或 setgid.
发表于 2003-5-2 13:01:01 | 显示全部楼层
精品!这种玩法才有意思,除了LFS在其它linux发布版下应该也可以用,管理自己的“小金库”,我去在Debian下试试..........
 楼主| 发表于 2003-5-2 13:24:36 | 显示全部楼层
没错,这个东西挺有意思的,研究中……
相关资源,是tushar T自己做的脚本,思路基本与上面的hint一致,不过增加了一些特性,比如记录编译啊这些,还能下载,自动安装。越看越像 portage 了。还没看懂怎么装……
http://linuxfromscratch.org/~tushar/build-scripts/
 楼主| 发表于 2003-5-3 05:55:11 | 显示全部楼层
写信问了tushar,他给了一点指导,继续研究……
下面是信的内容:


  1. Xinkui Hao wrote:

  2. >I've read the pkg user hint and your pkg-user-bridge scripts.I found
  3. >there are so many enhancement and want to immediately use your scripts
  4. >in my system.
  5. >
  6. >But,how do I install the first two package:pkg-user-bridge and
  7. >lfs-scripts?I've noticed that there is a script named Bootstrap,but it's
  8. >bootstrap to a new plfs build.In order to do that,firstly I need
  9. >lfs-scripts.Is it possibe that I simply copy files according to the
  10. >lfs-scripts.build file and don't undermine the ownerships etc?
  11. >  
  12. >
  13. Sorry, I have been putting off writing documentation for the build
  14. system:( But in my defense, there is a lot of "events" at my end and I
  15. didn't know anyone would be interested:) Hopefully this e-mail will act
  16. in lieu of a complete doc till I get time to make a formal doc with more
  17. detailed explainations.

  18. The first task would be to follow the Bootstrap script to create the
  19. stage1 build. This script in the data/lfs/lfs-scripts.tar.bz2 tarball.
  20. Since the Bootstrap command is coded to begin from a Pkg-user based
  21. system, it would be easiest to execute the commands manually to create
  22. the stage1 directory (PS: I renamed the /stage1 to /bootstrap).

  23. Once the /bootstrap directory is complete, create the partition for LFS,
  24. format it and move /bootstrap to $LFS/bootstrap.

  25. Also copy over the entire data directory from the website to
  26. /var/lib/pkg-user/data. I think the BootStrap hint does that, but I am
  27. not sure.

  28. Now follow the instructions in the ChrootLFS script to chroot into the
  29. LFS partition.

  30. Inside the chroot, run the Genesis script which will create all the
  31. things that you need to do before you begin with the installation of the
  32. first package (glibc).

  33. Following the order of the PLFS packages, install each package similar
  34. to the glibc installation discussed below.

  35.      * PkgInstall "GNU C Library" glibc -> This will create the pkg-user
  36.        for the package and set up the home directory, link to its build
  37.        directories, etc. Note that you will have to use the same pkg
  38.        names I used. Also you will need to copy the pkg tarballs over to
  39.        the home directory for each user after it is created.
  40.      * StandardBuild glibc 2.3.2 -> This will build glibc using the
  41.        instructions in glibc.build file. Check the Build command to
  42.        figure out how it does that. It creates installation logs, etc.

  43. The order you would need to follow is:

  44.      * lfs
  45.      * linux (The build file recognizes that this is the first time
  46.        installing and hence only installs the headers).
  47.      * glibc
  48.      * ...
  49.      * linux

  50. Note: The pkg-user user is the user that replaces the install user in
  51. MSB's pkg-user hint.

  52. Once you are done installing the packages, configure and install the
  53. bootloader.

  54. Note that I have replaced some of the packages in LFS with my choice of
  55. packages (pcre + metalog) for syslogd, inetutils for netkit, grub for
  56. (bin86 + lilo).

  57. Check out the scripts in lfs-scripts.tar.bz2 to know what each one does.
  58. Also, before installing each package it would be prudent to check out
  59. the build file to know what it does.

  60. Hope this helps, let me know if you run into any problems.

  61. --
  62. Tushar Teredesai
  63.    E-mail:    tushartATabbnmDOTcom
  64.    Extension: 5267


  65. --
  66. Unsubscribe: send email to [email]listar@linuxfromscratch.org[/email]
  67. and put 'unsubscribe blfs-support' in the subject header of the message


复制代码
 楼主| 发表于 2003-5-5 09:23:42 | 显示全部楼层
现在我的系统已经转变成pkg-user的啦,呵呵。
改了不少脚本。不知道有没有人要。
发表于 2005-3-22 19:38:55 | 显示全部楼层

感兴趣

我也觉得这是个很好的做法。


不知道楼主用了这个办法一年多之后,有没有新的看法?
回复 支持 反对

使用道具 举报

您需要登录后才可以回帖 登录 | 注册

本版积分规则

快速回复 返回顶部 返回列表