第5章 重新编译login去除对pam模块的依赖

在阅读这篇博文之前,建议先阅读我的前4篇博文,而且最好按顺序阅读:(不然可能会觉得我写得不知所云,呵呵!)

第1篇:Linux系统裁减之,制作一个极度精简的Linux-1 https://blog.51cto.com/linuxprince/2045703

第2篇:Linux系统裁减之,制作一个极度精简的Linux-2-用脚本实现自动拷贝命令和依赖库文件 https://blog.51cto.com/linuxprince/2046142

第3篇:Linux系统裁减之,制作一个极度精简的Linux-3-为精简的系统增加网络功能和关机重启功能 https://blog.51cto.com/linuxprince/2047373

第4篇:Linux系统裁减之,制作一个极度精简的Linux-4-使用mingetty绕开/bin/login https://blog.51cto.com/linuxprince/2049081


5.1 下载login程序源代码

        之前我在第4篇博文已经写过下载 login 程序源代码的方法,这篇文章省略。我把源码放在/root/Softwares/Source目录。

[root@CentOS5lsrv01 ~]# ll Softwares/Source/

total 2892

-rw-r--r-- 1 root root   19624 Apr  9  2007 mingetty-1.07-5.2.2.src.rpm

-rw-r--r-- 1 root root 2933601 Nov  9  2012 util-linux-2.13-0.59.el5_8.src.rpm

提示:login的源代码都放在包 util-linux-2.13-0.59.el5_8.src.rpm


5.2 安装并重新编译 login 源码

1步:安装 util-linux-2.13-0.59.el5_8.src.rpm

[root@CentOS5lsrv01 Source]# rpm -ivh util-linux-2.13-0.59.el5_8.src.rpm

[root@CentOS5lsrv01 SOURCES]# cd /usr/src/redhat/SOURCES/    ß进入红帽源码包的安装目录

[root@CentOS5lsrv01 SOURCES]# ll | grep util-linux-2.13-pre7.tar.bz2    ßlogin所在的源码包

-rw-rw-r--  1 root root 2534328 Mar 29  2006 util-linux-2.13-pre7.tar.bz2


2步:解压 util-linux-2.13-pre7.tar.bz2 包并进入包“util-linux-2.13-pre7”目录

[root@CentOS5lsrv01 SOURCES]# tar xf util-linux-2.13-pre7.tar.bz2   <--解压

[root@CentOS5lsrv01 SOURCES]# cd util-linux-2.13-pre7    <--进入源码目录


特别提示:按照以下”错误的第3步“的方法编译login程序是不行的(我之前使用这种方法编译),登陆时会提示“Login incorrect”,意思是密码错误(哪怕是你输入的密码完全正确),在错误的“第3步”后有正确的编译方法,保证能成功。

**********错误的第3步开始**********

3步:重新编译 login 程序

并设置好预备编译参数(去除对pam模块的依赖)

[root@CentOS5lsrv01 util-linux-2.13-pre7]# ./configure --without-pam    <--设置预编译参数,去除对pam模块的依赖

注意:使用本文的方法编译login程序,可以不添加参数“--without-pam”,下面的内容将会说明原因。

[root@CentOS5lsrv01 util-linux-2.13-pre7]# cd login-utils/    <--预编译成功后进入login程序的源码目录


重点提示:编译前先要修改login.c 源码文件,添加头文件locale.h,不加的话编译会报错:

[root@CentOS5lsrv01 login-utils]# gcc -o login login.c ../lib/setproctitle.c checktty.c -Wall -lcrypt -I ../include/

login.c: In function ‘main’:

login.c:327: warning: implicit declaration of function ‘setlocale’

login.c:327: error: ‘LC_ALL’ undeclared (first use in this function)

login.c:327: error: (Each undeclared identifier is reported only once

login.c:327: error: for each function it appears in.)

login.c:843: warning: passing argument 1 of ‘gettimeofday’ from incompatible pointer type

login.c: In function ‘dolastlog’:

login.c:1302: warning: passing argument 1 of ‘time’ from incompatible pointer type


打开login.c源代码文件:

[root@CentOS5lsrv01 login-utils]# vi login.c

    115 #include <sys/sysmacros.h>

    116 #include <linux/major.h>

    117

    118 #include <utmp.h>

    119

    120 #include <locale.h>             <--添加locale.h头文件

    121

    122 #ifdef HAVE_SECURITY_PAM_MISC_H

    123 #  include <security/pam_appl.h>

    124 #  include <security/pam_misc.h>

    125 #  define PAM_MAX_LOGIN_TRIES   3


由于login程序没有make文件,所以需要自己写编译命令:

[root@CentOS5lsrv01 login-utils]# gcc -o login login.c ../lib/setproctitle.c checktty.c -Wall -lcrypt -I ../include/

login.c: In function ‘main’:

login.c:842: warning: passing argument 1 of ‘gettimeofday’ from incompatible pointer type

login.c: In function ‘dolastlog’:

login.c:1301: warning: passing argument 1 of ‘time’ from incompatible pointer type

[root@CentOS5lsrv01 login-utils]# ll | grep login

-rwxr-xr-x 1 root root 36746 Dec  9 14:58 login       <--生成了login二进制文件

-rw-r--r-- 1 1000 1000 10481 Aug  2  2005 login.1

-rw-r--r-- 1 1000 1000 34478 Dec  2 06:36 login.c

-rw-r--r-- 1 1000 1000   247 Aug  2  2005 login.h

[root@CentOS5lsrv01 login-utils]# file login

login: ELF 64-bit LSB executable, AMD x86-64, version 1 (SYSV), for GNU/Linux 2.6.9, dynamically linked (uses shared libs), not stripped   <--二进制可执行文件login

**********错误的第3步结束**********


**********正确的第3步开始**********

第3步:重新编译login程序(需要稍微修改一下源代码)

进入包“util-linux-2.13-pre7”解压后的目录并设置预编译参数

[root@CentOS5lsrv01 ~]# cd /usr/src/redhat/SOURCES/util-linux-2.13-pre7

[root@CentOS5lsrv01 util-linux-2.13-pre7]# ./configure --enable-login-utils  --without-selinux

参数解释:

--enable-login-utils    build chfn, chsh, login, newgrp, vipw

意思是编译util-linux-2.13-pre7时生成chfn,chsh,login,newgrp,vipw几个二进制可执行文件。

--without-selinux        compile login-utils without SELinux support

意思是编译util-linux-2.13-pre7时不提供对 SELinux 的支持,本文生成的miniLinux系统没有SELinux,若不加此参数,编译会报如下错误:

chfn.c:44:29: error: selinux/selinux.h: No such file or directory

chfn.c:45:36: error: selinux/av_permissions.h: No such file or directory

chfn.c: In function ‘main’:

chfn.c:148: error: ‘PASSWD__CHFN’ undeclared (first use in this function)

chfn.c:148: error: (Each undeclared identifier is reported only once

chfn.c:148: error: for each function it appears in.)

chfn.c:149: error: ‘security_context_t’ undeclared (first use in this function)

chfn.c:149: error: expected ‘;’ before ‘user_context’

chfn.c:150: error: ‘user_context’ undeclared (first use in this function)

make[2]: *** [chfn.o] Error 1

make[2]: Leaving directory `/usr/src/redhat/SOURCES/util-linux-2.13-pre7/login-utils'

make[1]: *** [all-recursive] Error 1

make[1]: Leaving directory `/usr/src/redhat/SOURCES/util-linux-2.13-pre7'

make: *** [all] Error 2


下面先进入login-utils目录修改login程序的源代码文件"login.c",我这里只取出这篇文章最重要的部分源代码做一些摘要解释:

[root@CentOS5lsrv01 util-linux-2.13-pre7]# cd login-utils/

[root@CentOS5lsrv01 login-utils]# vi login.c

   117 #include <utmp.h>
   118 #include <shadow.h>    //添加头文件“shadow.h”,下面的结构体“spwd”定义在该文件
   //此处省略119行到651行的代码
   652          if ((pwd = getpwnam(username))) { //获取用户输入的用户名,如果用户名能在文件
                                                  // /etc/passwd中找到,则执行下面的代码
                                                  
   //653行到658行的意思是如果宏“SHADOW_PWD”定义了,则654行到657行的代码会被编译,宏“SHADOW_PWD”
   //应该是定义在某个头文件中,但本文使用的方法并没有使用相应的头文件。login程序的源码大量使用了类
   //似的条件编译代码,或者我找个时间详细了解下条件编译中的宏定义在哪些文件吧!!这里就暂不纠结了!
   
   653 // #  ifdef SHADOW_PWD    <--把这行注释掉。
   654              struct spwd *sp;//定义一个结构体指针,用于对应/etc/shadow文件的数据结构
   655              
   656              if ((sp = getspnam(username)))//根据用户输入的用户名,如果在/etc/shadow
                                                  //文件中查找到,则执行下面的代码
   657                pwd->pw_passwd = sp->sp_pwdp;//把从/etc/shadow文件中取出的密码加密密文
                                                   //赋值给pwd->pw_passwd
   658 // #  endif    <--把这行注释掉
   
   659              salt = pwd->pw_passwd;
   660          } else
   661            salt = "xx";
   662
   663          if (pwd) {
   664              initgroups(username, pwd->pw_gid);
   665              checktty(username, tty_name, pwd); /* in checktty.c */
   666          }
   667
   668          /* if user not super-user, check for disabled logins */
   669          if (pwd == NULL || pwd->pw_uid)
   670            checknologin();
   671
   672          /*
   673           * Disallow automatic login to root; if not invoked by
   674           * root, disallow if the uid's differ.
   675           */
   676          if (fflag && pwd) {
   677              int uid = getuid();
   678              
   679              passwd_req = pwd->pw_uid == 0 ||
   680                (uid && uid != pwd->pw_uid);
   681          }
   682
   683          /*
   684           * If trying to log in as root, but with insecure terminal,
   685           * refuse the login attempt.
   686           */
   687          if (pwd && pwd->pw_uid == 0 && !rootterm(tty_name)) {
   688              fprintf(stderr,
   689                      _("%s login refused on this terminal.\n"),
   690                      pwd->pw_name);
   691              
   692              if (hostname)
   693                syslog(LOG_NOTICE,
   694                       _("LOGIN %s REFUSED FROM %s ON TTY %s"),
   695                       pwd->pw_name, hostname, tty_name);
   696              else
   697                syslog(LOG_NOTICE,
   698                       _("LOGIN %s REFUSED ON TTY %s"),
   699                       pwd->pw_name, tty_name);
   700              continue;
   701          }
   702
   703          /*
   704           * If no pre-authentication and a password exists
   705           * for this user, prompt for one and verify it.
   706           */
   707          if (!passwd_req || (pwd && !*pwd->pw_passwd))
   708            break;
   709
   710          setpriority(PRIO_PROCESS, 0, -4);
   711          pp = getpass(_("Password: "));    //获取屏幕输入的密码赋值给变量 pp
   712
   713  #  ifdef CRYPTOCARD
   714          if (strncmp(pp, "CRYPTO", 6) == 0) {
   715              if (pwd && cryptocard()) break;
   716          }
   717  #  endif /* CRYPTOCARD */
   718
   719          p = crypt(pp, salt);//对用户通过屏幕输入的密码进行加密
   720          setpriority(PRIO_PROCESS, 0, 0);
   721
   //省略722到745行,都是关于KERBEROS认证方式的
   746          memset(pp, 0, strlen(pp));//出于安全的考虑,获取了用户输入密码并加密后清空
   747                                    //明文密码所在的内存区
   748          if (pwd && !strcmp(p, pwd->pw_passwd))//对比用户输入的密码与/etc/shadow文
   749              break;                            //件中记录的密码,如果匹配,就退出循环,
                                                      //表示认证成功,用户可以登陆啦!!

到此修改完成,保存退出,返回util-linux-2.13-pre7目录,直接编译:

[root@CentOS5lsrv01 ~]# cd /usr/src/redhat/SOURCES/util-linux-2.13-pre7

[root@CentOS5lsrv01 util-linux-2.13-pre7]# make

如果编译没有出错,就会看到 login-utils/目录下已经生成了login 二进制可执行文件

[root@CentOS5lsrv01 util-linux-2.13-pre7]# cd login-utils/

[root@CentOS5lsrv01 login-utils]# ll | grep login

-rwxr-xr-x 1 root root 68321 Jan  5 14:01 login <--生成了 login 二进制可执行文件

-rw-r--r-- 1 1000 1000 10481 Aug  2  2005 login.1

-rw-r--r-- 1 1000 1000 34701 Jan  5 14:01 login.c

-rw-r--r-- 1 root root 34458 Jan  4 23:31 login.c.bak

-rw-r--r-- 1 1000 1000   247 Aug  2  2005 login.h

-rw-r--r-- 1 root root 51992 Jan  5 14:01 login.o

 

**********正确的第3步结束**********


4步:重新拷贝 mingetty 程序到miniLinux系统

之前的测试中,miniLinux 系统使用了重新编译过的 mingetty 程序,这一步把它替换成红帽官方系统的 mingetty 程序,从源系统(CentOS5lsrv01)重新拷贝到miniLinux系统:

[root@CentOS5lsrv01 ~]# rm -f /mnt/sysroot/sbin/mingetty               <--删除编译过的mingetty

[root@CentOS5lsrv01 ~]# ./bincp.sh          <--重新拷贝源系统的mingetty程序到miniLinux系统

Input command what you want to copy to miniLinux:mingetty

Copy /sbin/mingetty Finished.

Continue input command to miniLinux:q

[root@CentOS5lsrv01 ~]#


5步:把编译好的 login 程序拷贝到miniLinux系统

[root@CentOS5lsrv01 ~]# ldd /bin/login     <--login程序有很多依赖的库文件呢!!

        linux-vdso.so.1 =>  (0x00007fff695fd000)

        libcrypt.so.1 => /lib64/libcrypt.so.1 (0x0000003979c00000)

        libpam.so.0 => /lib64/libpam.so.0 (0x000000397c800000)

        libpam_misc.so.0 => /lib64/libpam_misc.so.0 (0x000000397a400000)

        libaudit.so.0 => /lib64/libaudit.so.0 (0x000000397a000000)

        libc.so.6 => /lib64/libc.so.6 (0x0000003977400000)

        libdl.so.2 => /lib64/libdl.so.2 (0x0000003977800000)

        /lib64/ld-linux-x86-64.so.2 (0x0000003977000000)

[root@CentOS5lsrv01 ~]# ./bincp.sh          <--先拷贝源系统的login程序,下面再覆盖,偷懒嘛!!

Input command what you want to copy to miniLinux:login

Copy lib file /lib64/libcrypt.so.1 Finished.

Copy lib file /lib64/libpam.so.0 Finished.    <--注意,这里拷贝了两个关于 pam 的库,其实是不需要的,已经重新编译login

Copy lib file /lib64/libpam_misc.so.0 Finished.    <--去除了对 pam 模块的依赖,可以删除,看来还是不能偷懒了,呵呵!!

Copy lib file /lib64/libaudit.so.0 Finished.

Copy /bin/login Finished.

Continue input command to miniLinux:q


为miniLinux系统删除多余的 libpam.so.0 和 libpam_misc.so.0 文件:

[root@CentOS5lsrv01 ~]# rm -f /mnt/sysroot/lib64/libpam.so.0

[root@CentOS5lsrv01 ~]# rm -f /mnt/sysroot/lib64/libpam_misc.so.0


把编译好的login程序覆盖过去

[root@CentOS5lsrv01 ~]# cp -f /usr/src/redhat/SOURCES/util-linux-2.13-pre7/login-utils/login  /mnt/sysroot/bin/

cp: overwrite `/mnt/sysroot/bin/login'? y

[root@CentOS5lsrv01 ~]#


提示:第4步和第5步中使用的 bincp.sh 脚本是我编写的用于自动移植命令和命令依赖的库文件到 miniLinux 系统,详细可以查看我之前发布的第3篇博文:Linux系统裁减之,制作一个极度精简的Linux-3-为精简的系统增加网络功能和关机重启功能 https://blog.51cto.com/linuxprince/2047373,里面有bincp.sh的完整程序。


5.3 拷贝 nsswitch 的库文件到 minLinux 系统

nsswitch简介:

    nsswitch全称为: network service switch(网络服务转换),是一个通用框架,与各种类型存储交互的公共实现,实现名称解析服务。例如:用户名到ID,或者ID到用户名,再或者IP到域名的查找方式。nsswitch 加载了各存储的 API 接口,并以模块方式装载进 nsswitch 中,当程序发起对 nsswitch 的 API调用时,nsswitch 会自动完成到各种存储中查找内容。

Linux系统中nsswitch的库文件有很多,但本文实现的本地用户登陆只需要/lib64下的两个文件:

[root@CentOS5lsrv01 ~]# ll /lib64/libnss_files*

-rwxr-xr-x 1 root root 53880 Sep 16  2014 /lib64/libnss_files-2.5.so

lrwxrwxrwx 1 root root    19 Nov 29 09:53 /lib64/libnss_files.so.2 -> libnss_files-2.5.so


拷贝需要的libnss_files*文件到miniLinux系统

64位系统:

[root@CentOS5lsrv01 ~]# cp -d /lib64/libnss_files* /mnt/sysroot/lib64/

32位系统:

[root@CentOS5lsrv01 ~]# cp -d /lib/libnss_files* /mnt/sysroot/lib/

cp命令的“-d”参数让链接文件保持原来的链接文件属性



5.4为minLinux系统建立用户信息文件(/etc/pwsswd、/etc/group、/etc/shadow)

在源系统中移植用户root和redhat到miniLinux系统:

[root@CentOS5lsrv01 ~]# grep -E ^"root|redhat" /etc/passwd > /mnt/sysroot/etc/passwd

[root@CentOS5lsrv01 ~]# grep -E ^"root|redhat" /etc/group > /mnt/sysroot/etc/group

[root@CentOS5lsrv01 ~]# grep -E ^"root|redhat" /etc/shadow > /mnt/sysroot/etc/shadow

修改miniLinux系统中shadow文件权限:

[root@CentOS5lsrv01 ~]# chmod 400 /mnt/sysroot/etc/shadow

 

5.5为miniLinux系统建立nsswitch.conf配置文件

[root@CentOS5lsrv01 ~]# vi /mnt/sysroot/etc/nsswitch.conf

passwd: files       files:用户以本地认证方式登陆

shadow: files

group:  files


启动miniLinux系统测试吧(必须先挂起源系统,不要怪我唠叨,这步很重要),如下两图:

2018-03-01_21-40-16.png


2018-03-01_21-46-50.png

到此,大功告成啦!!噢耶!!!


小结:

1. 经历了7次修改,终于完美的实现了重新编译login程序。

2. 各位同行在网上找到的方法应该都是直接给出已经编译过的login程序的,没有详细编译过程,

貌似都是马哥给出的,我也看过马哥的教程哦!!

3. 从源码的层面去解决问题确实比较复杂,必须要有比较坚韧的精神才能坚持(貌似不小心赞了赞自己!!呵呵!)

4. 遇到问题不要轻易放弃,修改源代码我觉得难度还是比较大的(起码要将源码读几遍,了解了才能下手),但同时也更清楚Linux系统的工作原理,我觉得还是值得花时间的。

5. 希望本文提供的方法可以帮助到各位同行或对大家有借鉴作用。


最后如果各位同行小伙伴发现文章有错误或是有更好的方法,可以告诉我,大家一起交流,共同进步!谢谢!!