Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1753931
  • 博文数量: 199
  • 博客积分: 10
  • 博客等级: 民兵
  • 技术积分: 6186
  • 用 户 组: 普通用户
  • 注册时间: 2012-10-30 11:01
个人简介

Linuxer.

文章存档

2015年(4)

2014年(28)

2013年(167)

分类: LINUX

2013-04-01 13:36:55


点击(此处)折叠或打开

  1. int main(int argc, char **argv)
  2. {
  3.     extern int optind;
  4.     extern char *optarg, **environ;
  5.     struct group *gr;
  6.     register int ch;
  7.     register char *p;
  8.     int ask, fflag, hflag, pflag, cnt, errsv;
  9.     int quietlog, passwd_req;
  10.     char *domain, *ttyn;
  11.     char tbuf[MAXPATHLEN + 2], tname[sizeof(_PATH_TTY) + 10];
  12.     char *termenv;
  13.     char *childArgv[10];
  14.     char *buff;
  15.     int childArgc = 0;
  16. #ifdef HAVE_SECURITY_PAM_MISC_H
  17.     int retcode;
  18.     pam_handle_t *pamh = NULL;
  19.     struct pam_conv conv = { misc_conv, NULL };
  20.     pid_t childPid;
  21. #else
  22.     char *salt, *pp;
  23. #endif
  24. #ifdef LOGIN_CHOWN_VCS
  25.     char vcsn[20], vcsan[20];
  26. #endif
  27.     
  28.     pid = getpid();//得到当前进程的pid号
  29.     
  30.     signal(SIGALRM, timedout);//设置定时信号的处理函数timedout
  31.     alarm((unsigned int)timeout);//设置定时器timeout = 60s
  32.     signal(SIGQUIT, SIG_IGN);//忽略SIGQUIT信号
  33.     signal(SIGINT, SIG_IGN);//忽略SIGINT信号
  34.     
  35.     setlocale(LC_ALL, "");//本函数用来配置地域的信息,设置当前程序使用的本地化信息.使用系统默认的设置
  36.     bindtextdomain(PACKAGE, LOCALEDIR);
  37.     textdomain(PACKAGE);
  38.     
  39.     //用来设置进程执行优先权为0
  40.     setpriority(PRIO_PROCESS, 0, 0);
  41.     initproctitle(argc, argv);
  42.     
  43.     gethostname(tbuf, sizeof(tbuf));//得到主机名,存到tbuf中
  44.     xstrncpy(thishost, tbuf, sizeof(thishost));//将主机名复制到thishost中
  45.     domain = index(tbuf, '.');//得到域名"."隔开,#define index strchr,查找字符串tbuf中首次出现字符‘.’的位置
  46.     
  47.     username = tty_name = hostname = NULL;
  48.     fflag = hflag = pflag = 0;
  49.     passwd_req = 1;//表示输入的用户名需要进行密码验证,为0则不需密码验证
  50.     
  51.     while ((ch = getopt(argc, argv, "fh:p")) != -1)//分析命令行参数
  52.         switch (ch) {
  53.             case 'f':
  54.              fflag = 1;
  55.              break;
  56.             
  57.             case 'h':
  58.              if (getuid()) {
  59.              fprintf(stderr, _("login: -h for super-user only.\n"));
  60.              exit(1);
  61.              }
  62.              hflag = 1;
  63.              if (domain && (p = index(optarg, '.')) && strcasecmp(p, domain) == 0)
  64.              *p = 0;
  65.             
  66.              hostname = strdup(optarg);     /* strdup: Ambrose C. Li */
  67.              {
  68.                  struct hostent *he = gethostbyname(hostname);/* he points to static storage; copy the part we use */
  69.                  hostaddress[0] = 0;
  70.                  if (he && he->h_addr_list && he->h_addr_list[0])
  71.                      memcpy(hostaddress, he->h_addr_list[0],sizeof(hostaddress));
  72.              }
  73.              break;
  74.             
  75.             case 'p':
  76.              pflag = 1;
  77.              break;
  78.             
  79.             case '?':
  80.             default:
  81.              fprintf(stderr, _("usage: login [-fp] [username]\n"));
  82.              exit(1);
  83.         }
  84.     argc -= optind;
  85.     argv += optind;
  86.     if (*argv) {
  87.         char *p = *argv;
  88.         //strdup()在内部调用了malloc()为变量分配内存,不需要使用返回的字符串时,需要用free()释放相应的内存空间,否则会造成内存泄漏
  89.         username = strdup(p);//复制字符串给username,得到登录名
  90.         ask = 0;
  91.         while(*p)
  92.          *p++ = ' ';//登录名最后一个字符为空
  93.     } else
  94.         ask = 1;
  95.     
  96.     for (cnt = getdtablesize(); cnt > 2; cnt--)//返回所在进程的文件描述附表的项数,即该进程打开的文件数目。
  97.         close(cnt);//关闭打开的文件
  98.     
  99.     //用ttyname(0) 或 ttyname(1) 或 ttyname(2)是可以得到的,因为0、1、2分别是与具体终端相连的标准输入、标准输出和标准出错输出的文件描述符。所以可以得到。
  100.     ttyn = ttyname(0);//得到终端标示号
  101.     
  102.     if (ttyn == NULL || *ttyn == '\0') {
  103.         sprintf(tname, "%s??", _PATH_TTY);
  104.         ttyn = tname;
  105.     }
  106.     
  107.     //检查终端设备
  108.     check_ttyname(ttyn);
  109.     
  110.     //将终端设备名赋给tty_name
  111.     if (strncmp(ttyn, "/dev/", 5) == 0)
  112.         tty_name = ttyn+5;
  113.     else
  114.         tty_name = ttyn;
  115.     
  116.     //将终端设备号赋给tty_number
  117.     if (strncmp(ttyn, "/dev/tty", 8) == 0)
  118.         tty_number = ttyn+8;
  119.     else {
  120.         char *p = ttyn;
  121.         while (*p && !isdigit(*p)) p++;
  122.         tty_number = p;
  123.     }
  124.     
  125.     //将目前进程所属的组识别码设为目前进程的进程识别码。
  126.     setpgrp();    
  127.     //设置并打开终端设备
  128.     {
  129.         struct termios tt, ttt;
  130.         
  131.         tcgetattr(0, &tt);
  132.         ttt = tt;
  133.         ttt.c_cflag &= ~HUPCL;//清除关闭设备时挂起标志
  134.         
  135.         /* These can fail, e.g. with ttyn on a read-only filesystem */
  136.         chown(ttyn, 0, 0);
  137.         chmod(ttyn, TTY_MODE);
  138.         
  139.         /* Kill processes left on this tty */
  140.         tcsetattr(0,TCSAFLUSH,&ttt);
  141.         signal(SIGHUP, SIG_IGN);//忽略挂起信号,所以vhangup()不会杀死程序
  142.         vhangup();//挂起当前终端
  143.         signal(SIGHUP, SIG_DFL);//恢复
  144.         
  145.         //打开终端设备,标准输入,标准输出,出错都链接到终端设备上
  146.         opentty(ttyn);
  147.         tcsetattr(0,TCSAFLUSH,&tt);/* restore tty modes */
  148.     }
  149.     
  150.     openlog("login", LOG_ODELAY, LOG_AUTHPRIV);
  151.     
  152. #ifdef HAVE_SECURITY_PAM_MISC_H
  153.     retcode = pam_start("login",username, &conv, &pamh);//调用login的PAM配置文件,pam_handle_t pamh是保存所有pam信息的地方.由pam_start函数创建.这些信息,可以通过pam_set_item与pam_get_item来设置与获取.
  154.     if(retcode != PAM_SUCCESS) {
  155.         fprintf(stderr, _("login: PAM Failure, aborting: %s\n"),pam_strerror(pamh, retcode));
  156.         syslog(LOG_ERR, _("Couldn't initialize PAM: %s"),pam_strerror(pamh, retcode));
  157.         exit(99);
  158.     }
  159.     /* int pam_get_item(const pam_handle_t *pamh, int item_type, const void **item);
  160.      int pam_set_item(pam_handle_t *pamh, int item_type, const void **item);
  161.      通过该函数,可以得到的值有:
  162.      PAM_SERVICE:服务名
  163.      PAM_USER:用户名,该用户名,指的是在pam_start的第二个参数所设置的.也可以通过pam_set_item来设置
  164.      PAM_USER_PROMPT: 当提示输入用户名时的指示字符串.默认为login:
  165.      PAM_TTY: 用户程序对应的tty
  166.      PAM_RHOST: 该用户程序使用的 远程主机信息.
  167.      PAM_CONV: 该用户程序对应的pam_conv()函数. */
  168.     retcode = pam_set_item(pamh, PAM_RHOST, hostname);//设置PAM的远程主机信息.
  169.     PAM_FAIL_CHECK;
  170.     retcode = pam_set_item(pamh, PAM_TTY, tty_name);//设置用户程序对应的tty、
  171.     PAM_FAIL_CHECK;
  172.     
  173.     retcode = pam_set_item(pamh, PAM_USER_PROMPT, _("login: "));//当提示输入用户名时的指示字符串
  174.     PAM_FAIL_CHECK;
  175.     
  176.     /* if fflag == 1, then the user has already been authenticated */
  177.     if (fflag && (getuid() == 0))
  178.         passwd_req = 0;
  179.     else
  180.         passwd_req = 1;
  181.     
  182.     if(passwd_req == 1) {//需要验证登录用户名密码
  183.         int failcount=0;
  184.         pam_get_item(pamh, PAM_USER, (const void **) &username);//获得用户名保存在username中
  185.         if (!username)
  186.             pam_set_item(pamh, PAM_USER, NULL);
  187.         
  188.         retcode = pam_authenticate(pamh, 0);////进行auth类型认证,检查用户输入的密码是否正确
  189.         while((failcount++ < PAM_MAX_LOGIN_TRIES) &&((retcode == PAM_AUTH_ERR) ||(retcode == PAM_USER_UNKNOWN) ||(retcode == PAM_CRED_INSUFFICIENT) ||(retcode == PAM_AUTHINFO_UNAVAIL))) {
  190.             pam_get_item(pamh, PAM_USER, (const void **) &username);
  191.         
  192.             syslog(LOG_NOTICE,_("FAILED LOGIN %d FROM %s FOR %s, %s"),failcount, hostname, username, pam_strerror(pamh, retcode));
  193.             logbtmp(tty_name, username, hostname);
  194.         
  195.             fprintf(stderr,_("Login incorrect\n\n"));
  196.             pam_set_item(pamh,PAM_USER,NULL);
  197.             retcode = pam_authenticate(pamh, 0);
  198.         }
  199.         
  200.         if (retcode != PAM_SUCCESS) {//达到一定的出错次数,则退出
  201.             pam_get_item(pamh, PAM_USER, (const void **) &username);
  202.         
  203.             if (retcode == PAM_MAXTRIES)
  204.                 syslog(LOG_NOTICE,_("TOO MANY LOGIN TRIES (%d) FROM %s FOR ""%s, %s"), failcount, hostname, username,pam_strerror(pamh, retcode));
  205.             else
  206.                 syslog(LOG_NOTICE,_("FAILED LOGIN SESSION FROM %s FOR %s, %s"),hostname, username, pam_strerror(pamh, retcode));
  207.             logbtmp(tty_name, username, hostname);
  208.         
  209.             fprintf(stderr,_("\nLogin incorrect\n"));
  210.             pam_end(pamh, retcode);
  211.             exit(0);
  212.         }
  213.         
  214.         retcode = pam_acct_mgmt(pamh, 0);//进行account类型认证,通过了密码认证之后再调用帐号管理API,检查用户帐号是否已经过期
  215.         
  216.         if(retcode == PAM_NEW_AUTHTOK_REQD) {
  217.             retcode = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
  218.         }
  219.         
  220.         PAM_FAIL_CHECK;
  221.     }
  222.     //再次取得用户名
  223.     retcode = pam_get_item(pamh, PAM_USER, (const void **) &username);
  224.     PAM_FAIL_CHECK;
  225.     
  226.     if (!username || !*username) {//为空的话出错退出
  227.         fprintf(stderr, _("\nSession setup problem, abort.\n"));
  228.         syslog(LOG_ERR, _("NULL user name in %s:%d. Abort."),__FUNCTION__, __LINE__);
  229.         pam_end(pamh, PAM_SYSTEM_ERR);
  230.         exit(1);
  231.     }
  232.     if (!(pwd = getpwnam(username))) {//获取用户登录相关信息,读取口令文件/etc/passwd获得username的登录相关信息
  233.         fprintf(stderr, _("\nSession setup problem, abort.\n"));
  234.         syslog(LOG_ERR, _("Invalid user name \"%s\" in %s:%d. Abort."),username, __FUNCTION__, __LINE__);
  235.         pam_end(pamh, PAM_SYSTEM_ERR);
  236.         exit(1);
  237.     }
  238.     
  239.     //复制用户登录信息
  240.     memcpy(&pwdcopy, pwd, sizeof(*pwd));
  241.     pwd = &pwdcopy;
  242.     pwd->pw_name = strdup(pwd->pw_name);
  243.     pwd->pw_passwd = strdup(pwd->pw_passwd);
  244.     pwd->pw_gecos = strdup(pwd->pw_gecos);
  245.     pwd->pw_dir = strdup(pwd->pw_dir);
  246.     pwd->pw_shell = strdup(pwd->pw_shell);
  247.     if (!pwd->pw_name || !pwd->pw_passwd || !pwd->pw_gecos ||!pwd->pw_dir || !pwd->pw_shell) {
  248.         fprintf(stderr, _("login: Out of memory\n"));
  249.         syslog(LOG_ERR, "Out of memory");
  250.         pam_end(pamh, PAM_SYSTEM_ERR);
  251.         exit(1);
  252.     }
  253.     username = pwd->pw_name;
  254.     
  255.     //因为每个用户可以属于多个组,该函数将user所属的所有组还有group都添加到当前运行进程的有效组,即将这些要添加的组作为添加组。
  256.     if (initgroups(username, pwd->pw_gid) < 0) {
  257.         syslog(LOG_ERR, "initgroups: %m");
  258.         fprintf(stderr, _("\nSession setup problem, abort.\n"));
  259.         pam_end(pamh, PAM_SYSTEM_ERR);
  260.         exit(1);
  261.     }
  262.     
  263.     retcode = pam_open_session(pamh, 0);//通过帐户管理检查之后则打开会话
  264.     PAM_FAIL_CHECK;
  265.     
  266.     retcode = pam_setcred(pamh, PAM_ESTABLISH_CRED);//建立认证服务的用户证书
  267.     PAM_FAIL_CHECK;
  268.     
  269. #else//若是没有定义PAM机制,则进行以下操作
  270.     
  271.     for (cnt = 0;; ask = 1) {
  272.         if (ask) {
  273.             fflag = 0;
  274.             getloginname();//获得用户名,存在username中
  275.         }
  276.     
  277.         if (username[0] == '+') {
  278.             puts(_("Illegal username"));
  279.             badlogin(username);
  280.             sleepexit(1);
  281.         }
  282.         //取得用户登录相关信息
  283.         if ((pwd = getpwnam(username))) {//读取口令文件/etc/passwd获得登录相关信息
  284.             salt = pwd->pw_passwd;//加密口令
  285.         } else
  286.             salt = "xx";
  287.     
  288.         if (pwd) {
  289.             initgroups(username, pwd->pw_gid);
  290.             checktty(username, tty_name, pwd); /* in checktty.c */
  291.         }
  292.     
  293.     /* if user not super-user, check for disabled logins */
  294.         if (pwd == NULL || pwd->pw_uid)
  295.             checknologin();
  296.     
  297.     /*
  298.     * Disallow automatic login to root; if not invoked by
  299.     * root, disallow if the uid's differ.
  300.     */
  301.         if (fflag && pwd) {
  302.             int uid = getuid();
  303.             passwd_req = pwd->pw_uid == 0 ||(uid && uid != pwd->pw_uid);
  304.         }
  305.     
  306.         if (pwd && pwd->pw_uid == 0 && !rootterm(tty_name)) {
  307.             fprintf(stderr,_("%s login refused on this terminal.\n"),pwd->pw_name);
  308.         
  309.             if (hostname)
  310.                 syslog(LOG_NOTICE,_("LOGIN %s REFUSED FROM %s ON TTY %s"),pwd->pw_name, hostname, tty_name);
  311.             else
  312.                 syslog(LOG_NOTICE,_("LOGIN %s REFUSED ON TTY %s"),pwd->pw_name, tty_name);
  313.             continue;
  314.         }
  315.     
  316.         //若passwd_req设置于为0(表示不需输入密码,直接登录),或没有密码则不需登录比较密码,直接登录
  317.         if (!passwd_req || (pwd && !*pwd->pw_passwd))
  318.             break;
  319.     
  320.         setpriority(PRIO_PROCESS, 0, -4);//设置进程优先级    
  321.         pp = getpass(_("Password: "));//读入密码
  322.         
  323.         //crypt返回使用 DES、Blowfish 或 MD5 加密的字符串
  324.         //key:要加密的明文。salt:密钥。
  325.         p = crypt(pp, salt);
  326.         setpriority(PRIO_PROCESS, 0, 0);//设置进程优先级    
  327.         memset(pp, 0, strlen(pp));//清空pp
  328.         
  329.         if (pwd && !strcmp(p, pwd->pw_passwd))//若是密码正确就退出for循环,不用再输入用户名和密码再判断
  330.             break;
  331.         
  332.         //密码不正确,往下处理
  333.         printf(_("Login incorrect\n"));
  334.         badlogin(username); /* log ALL bad logins */
  335.         failures++;
  336.     
  337.         //错误超过10次会退出
  338.         if (++cnt > 3) {
  339.             if (cnt >= 10) {
  340.                 sleepexit(1);
  341.             }
  342.             sleep((unsigned int)((cnt - 3) * 5));
  343.         }
  344.     }
  345. #endif /* !HAVE_SECURITY_PAM_MISC_H */

  346.     //往下去。表示用户名和密码匹配成功
  347.     alarm((unsigned int)0);//turn off timeout
  348.     endpwent();//用来关闭由所打开的密码文件/etc/passwd
  349.     
  350.     {
  351.         char tmpstr[MAXPATHLEN];
  352.         uid_t ruid = getuid();//返回一个调用程序的真实用户ID
  353.         gid_t egid = getegid();//用来取得执行目前进程有效组识别码
  354.         
  355.         //比较初始工作目录长度是否大于设定的长度
  356.         if (strlen(pwd->pw_dir) + sizeof(_PATH_HUSHLOGIN) + 2 > MAXPATHLEN)
  357.             quietlog = 0;
  358.         else {
  359.             sprintf(tmpstr, "%s/%s", pwd->pw_dir, _PATH_HUSHLOGIN);
  360.             setregid(-1, pwd->pw_gid);//rgid设为目前进程的真实组识别码(不变),将pwd->pw_gid设为目前进程的真实组识别码
  361.             setreuid(0, pwd->pw_uid);//
  362.             quietlog = (access(tmpstr, R_OK) == 0);//初始目录可访问,则quietlog置为1
  363.             setuid(0);//设置实际用户ID和有效用户ID
  364.             setreuid(ruid, 0);
  365.             setregid(-1, egid);
  366.         }
  367.     }
  368.     
  369.     //登录账户记录
  370.     //utmp记录当前登录进系统的各个用户;wtmp文件跟踪各个登录和注销事件
  371.     {
  372.         struct utmp ut;
  373.         struct utmp *utp;
  374.         
  375.         utmpname(_PATH_UTMP);
  376.         setutent();//打开utmp文件
  377.         
  378.         while ((utp = getutent()))//读出一个记录
  379.             if (utp->ut_pid == pid&& utp->ut_type >= INIT_PROCESS&& utp->ut_type <= DEAD_PROCESS)
  380.                 break;
  381.         
  382.         if (utp == NULL) {
  383.             setutent();
  384.             ut.ut_type = LOGIN_PROCESS;
  385.             strncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
  386.             utp = getutline(&ut);
  387.         }
  388.         
  389.         if (utp) {
  390.             memcpy(&ut, utp, sizeof(ut));
  391.         } else {
  392.             memset(&ut, 0, sizeof(ut));
  393.         }
  394.     
  395.         if (ut.ut_id[0] == 0)
  396.             strncpy(ut.ut_id, tty_number, sizeof(ut.ut_id));
  397.         
  398.         strncpy(ut.ut_user, username, sizeof(ut.ut_user));
  399.         xstrncpy(ut.ut_line, tty_name, sizeof(ut.ut_line));
  400.         time_t t;
  401.         time(&t);
  402.         ut.ut_time = t;//设置登录时间
  403.         ut.ut_type = USER_PROCESS;
  404.         ut.ut_pid = pid;
  405.         if (hostname) {
  406.             xstrncpy(ut.ut_host, hostname, sizeof(ut.ut_host));
  407.             if (hostaddress[0])
  408.                 memcpy(&ut.ut_addr, hostaddress, sizeof(ut.ut_addr));
  409.         }
  410.     
  411.         pututline(&ut);
  412.         endutent();//关闭文件
  413.     
  414.     //更新wtmp文件
  415. #if HAVE_UPDWTMP
  416.         updwtmp(_PATH_WTMP, &ut);
  417. #else
  418.         {
  419.             int lf, wtmp;
  420.             if ((lf = open(_PATH_WTMPLOCK, O_CREAT|O_WRONLY, 0660)) >= 0) {
  421.                 flock(lf, LOCK_EX);
  422.                 if ((wtmp = open(_PATH_WTMP, O_APPEND|O_WRONLY)) >= 0) {
  423.                     write(wtmp, (char *)&ut, sizeof(ut));
  424.                     close(wtmp);
  425.                 }
  426.                 flock(lf, LOCK_UN);
  427.                 close(lf);
  428.             }
  429.         }
  430. #endif
  431.     }
  432.     
  433.     dolastlog(quietlog);
  434.     
  435.     chown(ttyn, pwd->pw_uid,(gr = getgrnam(TTYGRPNAME)) ? gr->gr_gid : pwd->pw_gid);
  436.     chmod(ttyn, TTY_MODE);
  437.     
  438.     setgid(pwd->pw_gid);
  439.     
  440.     if (*pwd->pw_shell == '\0')
  441.         pwd->pw_shell = _PATH_BSHELL;
  442.     
  443.     /* preserve TERM even without -p flag */
  444.     {
  445.         char *ep;        
  446.         if(!((ep = getenv("TERM")) && (termenv = strdup(ep))))
  447.             termenv = "dumb";
  448.     }
  449.     
  450.     /* destroy environment unless user has requested preservation */
  451.     if (!pflag)
  452.     {
  453.      environ = (char**)malloc(sizeof(char*));
  454.         memset(environ, 0, sizeof(char*));
  455.     }
  456.     
  457.     //设置shell的环境变量
  458.     setenv("HOME", pwd->pw_dir, 0); /* legal to override */
  459.     if(pwd->pw_uid)
  460.         setenv("PATH", _PATH_DEFPATH, 1);
  461.     else
  462.         setenv("PATH", _PATH_DEFPATH_ROOT, 1);
  463.     
  464.     setenv("SHELL", pwd->pw_shell, 1);
  465.     setenv("TERM", termenv, 1);
  466.     
  467.     {
  468.         char tmp[MAXPATHLEN];
  469.         if (sizeof(_PATH_MAILDIR) + strlen(pwd->pw_name) + 1 < MAXPATHLEN) {
  470.             sprintf(tmp, "%s/%s", _PATH_MAILDIR, pwd->pw_name);
  471.             setenv("MAIL",tmp,0);
  472.         }
  473.     }
  474.     
  475.     setenv("LOGNAME", pwd->pw_name, 1);
  476.     
  477. #ifdef HAVE_SECURITY_PAM_MISC_H
  478.     {
  479.         int i;
  480.         char ** env = pam_getenvlist(pamh);//获取pam环境变量
  481.         
  482.         if (env != NULL) {
  483.             for (i=0; env[i]; i++) {
  484.                 putenv(env[i]);//把字符串加到当前环境中
  485.             }
  486.         }
  487.     }
  488. #endif
  489.     
  490.     setproctitle("login", username);
  491.     
  492.     if (!strncmp(tty_name, "ttyS", 4))
  493.         syslog(LOG_INFO, _("DIALUP AT %s BY %s"), tty_name, pwd->pw_name);
  494.     
  495.     if (pwd->pw_uid == 0) {
  496.         if (hostname)
  497.             syslog(LOG_NOTICE, _("ROOT LOGIN ON %s FROM %s"),tty_name, hostname);
  498.         else
  499.             syslog(LOG_NOTICE, _("ROOT LOGIN ON %s"), tty_name);
  500.     } else {
  501.         if (hostname)
  502.             syslog(LOG_INFO, _("LOGIN ON %s BY %s FROM %s"), tty_name, pwd->pw_name, hostname);
  503.         else
  504.             syslog(LOG_INFO, _("LOGIN ON %s BY %s"), tty_name, pwd->pw_name);
  505.     }
  506.     
  507.     if (!quietlog) {
  508.         motd();    
  509.     }
  510.     
  511.     signal(SIGALRM, SIG_DFL);
  512.     signal(SIGQUIT, SIG_DFL);
  513.     signal(SIGTSTP, SIG_IGN);
  514.     
  515. #ifdef HAVE_SECURITY_PAM_MISC_H    
  516.     childPid = fork();//创建子进程
  517.     if (childPid < 0) {
  518.         int errsv = errno;
  519.         fprintf(stderr, _("login: failure forking: %s"), strerror(errsv));
  520.         PAM_END;
  521.         exit(0);
  522.     }
  523.     
  524.     if (childPid) {//父进程,等待子进程退出
  525.     /* parent - wait for child to finish, then cleanup session */
  526.         signal(SIGHUP, SIG_IGN);
  527.         signal(SIGINT, SIG_IGN);
  528.         signal(SIGQUIT, SIG_IGN);
  529.         signal(SIGTSTP, SIG_IGN);
  530.         signal(SIGTTIN, SIG_IGN);
  531.         signal(SIGTTOU, SIG_IGN);//忽略以上信号
  532.         
  533.         wait(NULL);//等待子进程结束
  534.         PAM_END;//PAM结束
  535.         exit(0);
  536.     }
  537.     //以下是子进程/* child */
  538.     
  539.     /* start new session */
  540.     setsid();//进程成为新的会话组长和新的进程组长,并与原来的登录会话和进程组脱离。由于会话过程对控制终端的独占性,进程同时与控制终端脱离。
  541.     
  542.     /* make sure we have a controlling tty */
  543.     opentty(ttyn);//子进程打开设备终端
  544.     openlog("login", LOG_ODELAY, LOG_AUTHPRIV);    //重新打开log文件
  545.     
  546.     //TIOCSCTTY: steal tty from other process group
  547.     if (ioctl(0, TIOCSCTTY, 1))
  548.         syslog(LOG_ERR, _("TIOCSCTTY failed: %m"));        
  549. #endif

  550.     signal(SIGINT, SIG_DFL);//设置信号为默认处理
  551.     
  552.     //设置实际用户ID和有效用户ID
  553.     if(setuid(pwd->pw_uid) < 0 && pwd->pw_uid) {
  554.         syslog(LOG_ALERT, _("setuid() failed"));
  555.         exit(1);
  556.     }
  557.     
  558.     //进入到初始工作目录
  559.     if (chdir(pwd->pw_dir) < 0) {
  560.         printf(_("No directory %s!\n"), pwd->pw_dir);
  561.         if (chdir("/"))
  562.             exit(0);
  563.         pwd->pw_dir = "/";
  564.         printf(_("Logging in with home = \"/\".\n"));
  565.     }
  566.     
  567.     //给shell执行命令的字符串分配空间
  568.     if (strchr(pwd->pw_shell, ' ')) {
  569.         buff = malloc(strlen(pwd->pw_shell) + 6);
  570.         if (!buff) {
  571.          fprintf(stderr, _("login: no memory for shell script.\n"));
  572.          exit(0);
  573.         }
  574.     
  575.         strcpy(buff, "exec ");
  576.         strcat(buff, pwd->pw_shell);
  577.         childArgv[childArgc++] = "/bin/sh";
  578.         childArgv[childArgc++] = "-sh";
  579.         childArgv[childArgc++] = "-c";
  580.         childArgv[childArgc++] = buff;
  581.     } else {
  582.         tbuf[0] = '-';
  583.         xstrncpy(tbuf + 1, ((p = rindex(pwd->pw_shell, '/')) ?p + 1 : pwd->pw_shell),sizeof(tbuf)-1);    
  584.         childArgv[childArgc++] = pwd->pw_shell;
  585.         childArgv[childArgc++] = tbuf;
  586.     }
  587.     
  588.     childArgv[childArgc++] = NULL;
  589.     //登录成功,执行/bin/sh进入shell
  590.     execvp(childArgv[0], childArgv + 1);
  591.     
  592.     errsv = errno;
  593.     
  594.     if (!strcmp(childArgv[0], "/bin/sh"))
  595.         fprintf(stderr, _("login: couldn't exec shell script: %s.\n"),strerror(errsv));
  596.     else
  597.         fprintf(stderr, _("login: no shell: %s.\n"), strerror(errsv));
  598.     
  599.     exit(0);
  600. }

阅读(4743) | 评论(0) | 转发(1) |
给主人留下些什么吧!~~