Linux登录验证机制、SSH Bruteforce Login学习

相关学习资料

http://files.cnblogs.com/LittleHann/linux%E4%B8%AD%E7%94%A8%E6%88%B7%E7%99%BB%E5%BD%95%E8%AE%A4%E8%AF%81%E6%9C%BA%E5%88%B6%E7%9A%84%E7%A0%94%E7%A9
%B6.rar
http://blog.chinaunix.net/uid-20196318-id-94771.html http://wenku.baidu.com/view/e3c66fdc50e2524de5187eda.html https://www.ibm.com/developerworks/cn/linux/l-pam/ https://www.ibm.com/developerworks/cn/linux/l-pam/#resources http://linux.chinaunix.net/techdoc/beginner/2008/11/17/1045867.shtml https://help.ubuntu.com/10.04/serverguide/kerberos.html http://blog.jobbole.com/22367/ http://www.ibm.com/developerworks/cn/linux/l-radius/ http://www3.imperial.ac.uk/ict/services/security/securityservices/publickeyinfrastructure/installcaonlinux https://wiki.archlinux.org/index.php/S/KEY_Authentication http://en.wikipedia.org/wiki/S/KEY http://blog.chinaunix.net/uid-773723-id-152230.html http://blog.lizhigang.net/archives/249 http://www.360doc.com/content/12/0502/11/9523733_208062914.shtml http://nichael1983.blog.163.com/blog/static/114969433201002711850400/ http://docstore.mik.ua/orelly/networking_2ndEd/ssh/ch11_04.htm http://blog.scottlowe.org/2006/08/21/native-kerberos-authentication-with-ssh/ http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.security/doc/security/using_openssh_with_kerberosv5.htm http://www.slac.stanford.edu/comp/unix/sshGSSAPI.html http://www.linuxmanpages.com/man5/sshd_config.5.php

 

目录

1. Linux中的登录验证机制
2. SSH中的登录验证机制
3. SSH Bruteforce Login的相关方法
4. Linux PAM Module Code Example

 

Linux中的登录验证机制

谈及"登录验证"这个概念,小瀚觉得应该要在不同的场景下进行具体讨论,Linux中的登录验证机制可以分为以下几类:

1. 系统级的登录验证:

系统级的登录指的是我们登录Linux系统时显示的那个login: 提示,要登录tty终端,我们就必须通过这个登录。这是一个系统级的登录验证(和windows下的winLogin.exe是一个概念)

linux系统里管理用户及密码的两个重要的文件

/etc/passwd
/etc/shadow

/etc/passwd包含各个用户的信息,linux平台包含七个字段,各个字段间用冒号隔开,分别是:

用户名:密码:用户id:组id:用户描述:用户家目录:用户的登录shell
cat /etc/passwd

测试代码: passwd.c

#include <grp.h>
#include <pwd.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>

int main (void)
{
  uid_t me;
  struct passwd *my_passwd;

  /* Get information about the user ID.  */
  me = getuid ();
  my_passwd = getpwuid (me);
  if (!my_passwd)
  {
      printf ("Couldn't find out about user %d.\n", (int) me);
      exit (EXIT_FAILURE);
  }

  /* Print the information.  */
  printf ("My login name is %s.\n", my_passwd->pw_name);
  printf ("My passwd is %s.\n", my_passwd->pw_passwd);
  printf ("My uid is %d.\n", (int) (my_passwd->pw_uid));
  printf ("My gid is %d.\n", (int) (my_passwd->pw_gid));
  printf ("My Real name is %s.\n", my_passwd->pw_gecos);
  printf ("My home directory is %s.\n", my_passwd->pw_dir);
  printf ("My default shell is %s.\n", my_passwd->pw_shell);
 
  return EXIT_SUCCESS;
}

/etc/shadow包含用户的密码信息,在linux中包含九项内容,分别是:

用户登录名:加密口令:这个时间是从1970年01月01日算起到最近一次修改口令的时间间隔(天数): 两次修改口令间隔最少的天数;如果设置为0,则禁用此功能: 两次修改口令间隔最多的天数: 
提前多少天警告用户口令将过期: 在口令过期之后多少天禁用此用户: 用户过期日期: 保留字段,目前为空,以备将来Linux发展之用
cat /etc/shadow

测试代码: shadow.c

#include <stdio.h>
#include <stdlib.h>
#include <pwd.h>
#include <shadow.h>

/*
 * Print information from the password entry
 * about the user name given as argv[1].
 */

int main( int argc, char** argv ) 
{
    struct spwd* sp; 
    if (argc < 2) 
    {
      printf("%s username \n", argv[0]);
      return(EXIT_FAILURE);
    }

    if( ( sp = getspnam( argv[1] ) ) == (struct spwd*)0) 
    {
      fprintf( stderr, "getspnam: unknown %s\n", argv[1] );
      return( EXIT_FAILURE );
    }
    printf( "login name  %s\n", sp->sp_namp );
    printf( "password    %s\n", sp->sp_pwdp );
    printf( "last password change    %d\n", sp->sp_lstchg );
    printf( "days until change allowed    %d\n", sp->sp_min );
    printf( "days before change required    %d\n", sp->sp_max );
    printf( "days warning for expiration    %d\n", sp->sp_warn );
    printf( "days before account inactive    %d\n", sp->sp_inact );
    printf( "date when account expires    %d\n", sp->sp_expire );
    printf( "reserved for future use    %d\n", sp->sp_flag ); 

    return( EXIT_SUCCESS );
}

关于Linux的系统级login登录验证的原理学习,请参阅"相关资料中给出的链接"

2. 第三方应用程序登录验证机制

对于Linux上的很多应用程序(windows上也一样),应用程序可以自己维护一套身份认证信息库是很普遍的,这种密码验证机制的方式、加密的算法、存储的方式很大程度上取决于开发软件的程序员的想法。同时,第三方应用也可以以系统中的核心密码文件(/etc/passwd、/etc/shadow)作为身份信息库,这样,用户在登录的时候,需要输入的就是Linux系统本身的账户密码。总体来说,有一下几种身份验证方式

1) 将账户信息保存在磁盘的xx.ini配置文件中(加密方式可选)
2) 将账户信息保存在本地、或者远程的数据库中(在mysql中建立相应的库、表)
3) 利用Linux自身固有的账户密码文件作为身份信息库(/etc/passwd、/etc/shadow)(实现方式请参阅1)

3. PAM身份验证

PAM(插入式验证模块(Pluggable Authentication Module,PAM))
简单来说,就是提供了一组身份验证、密码验证的统一抽象接口,应用程序员可以使用这些API接口来实现与安全性相关的功能,例如:

1) 用户验证
2) 数据加密
3) LDAP

相对于传统的应用系统各自生成自己独立的身份验证机制,使用PAM架构,安全性、兼容性、可扩展性都更好,因为PAM机制将多个低级别验证模式继承到高级别API中,该API将允许以独立于底层验证模式的方式编写验证模块。我们知道,既然PAM的目标是可扩展性、封装底层实现细节,则它的整体表现基本上会表现为可插拔的模块的形式,即PAM提供一个承上启下的基础环境,向下针对不同的密码存储、验证方式分别进行了模块实现,向上暴露出的API接口则针对每一种具体的应用封装了一个API模块,当某一个应用要使用PAM的时候,只需要动态地加载这个模块即可。

PAM中的动态验证配置信息保存在/etc/pam.d 或 /etc/pam.conf中

cat /etc/pam.d/login
#%PAM-1.0
auth [user_unknown=ignore success=ok ignore=ignore default=bad] pam_securetty.so
auth       include      system-auth
account    required     pam_nologin.so
account    include      system-auth
password   include      system-auth
# pam_selinux.so close should be the first session rule
session    required     pam_selinux.so close
session    optional     pam_keyinit.so force revoke
session    required     pam_loginuid.so
session    include      system-auth
session    optional     pam_console.so
# pam_selinux.so open should only be followed by sessions to be executed in the user context
session    required     pam_selinux.so open

PAM 模块的基本流程

显示 PAM 如何处理配置文件的流程图

PAM模块的结构(例如/etc/pam.d/passwd)
PAM 模块是按模块类型归类的。任何给定的模块至少要实现四种模块类型功能之一:

1. 验证模块(auth): 用于验证用户或设置/销毁凭证
2. 帐户管理模块(account): 将执行与访问、帐户及凭证有效期、密码限制/规则等有关的操作
3. 会话管理模块(session): 用于初始化和终止会话。
4. 密码管理模块(password)将执行与密码更改/更新有关的操作

之前说过,PAM的整体架构是一个承上启下的架构,在上层是针对不同的应用系统提供不同的动态加载模块,在/etc/pam.d/xxx中可以对它们进行配置。而在下层,PAM需要封装对密码验证、身份验证、访问控制等功能的代码模块
PAM将提供不同的功能,例如单点登录验证、访问控制等。每个功能的实现都是由不同的模块处理的。下面是一些主要模块:

1. pam_access 将使用登录名/域名,根据 /etc/security/access.conf 中的预定义规则交付日志守护进程样式的登录访问控制。
2. pam_cracklib 将根据密码规则检查密码。
3. pam_env sets/unsets 环境变量来自 /etc/security/pam_env_conf。
4. pam_debug 将调试 PAM。
5. pam_deny 将拒绝 PAM 模块。
6. pam_echo 将打印消息。
7. pam_exec 将执行外部命令。
8. pam_ftp 是匿名访问模块。
9. pam_localuser 要求将用户列于 /etc/passwd 中。
10. pam_unix 将通过 /etc/passwd 提供传统密码验证。

4. 基于网络的中心式身份验证

基于网络的中心式身份验证常常表现为一个中心服务器(中心式的网状结构),上面保存了所有账户的身份信息,应用程序在验证待登录用户的身份时,直接将原始请求发送到中心服务器进行集中式验证,然后根据验证结果决定此用户是否能登录。

集中式身份认证的技术主要有以下几种:

1. Kerberos
Kerberos协议主要用于计算机网络的身份鉴别(Authentication), 其特点是用户只需输入一次身份验证信息就可以凭借此验证获得的票据(ticket-granting ticket)访问多个服务,即SSO
(Single Sign On)。由于在每个Client和Service之间建立了共享密钥,使得该协议具有相当的安全性 http:
//linux.chinaunix.net/techdoc/beginner/2008/11/17/1045867.shtml https://help.ubuntu.com/10.04/serverguide/kerberos.html 2. RADIUS(Remote Authentication Dial-In User Service) Remote Authentication Dial-In User Service 协议是在 IETF 的 RFC 2865 中定义的。它允许网络访问服务器(NAS)执行对用户的验证、授权和记帐。RADIUS 是基于 UDP 的一种客户
机/服务器协议。RADIUS 客户机是网络访问服务器,它通常是一个路由器、交换机或无线访问点(访问点是网络上专门配置的节点;WAP 是无线版本)。RADIUS 服务器通常是在 UNIX 或
Windows 2000 服务器上运行的一个监护程序 功能完整的 RADIUS 服务器可以支持很多不同的用户验证机制,包括: 1) LDAP 2) PAP(Password Authentication Protocol,密码验证协议,与 PPP 一起使用,在此机制下,密码以明文形式被发送到客户机进行比较) 3) CHAP(Challenge Handshake Authentication Protocol,挑战握手验证协议,比 PAP 更安全,它同时使用用户名和密码) 4) 本地 UNIX/Linux 系统密码数据库(/etc/passwd) 5) 其他本地数据库(Mysql、postgreSQL) http://blog.jobbole.com/22367/ http://www.ibm.com/developerworks/cn/linux/l-radius/ 3. CA(Certificate Authority) CA是一种基于非对称加密算法的证书服务,CA体制中的"根服务器"就是所谓的中心认证服务器,服务所有子节点的证书颁发、身份认证 http://www3.imperial.ac.uk/ict/services/security/securityservices/publickeyinfrastructure/installcaonlinux

 

SSH中的登录验证机制

在基本了解了Linux下的登录身份验证后,我们接下来学习一下SSH中都有哪些身份验证机制。这里所指的SSH都是指在Linux、Unix上普遍使用的openSSH开源软件

SSH的配置文件所在位置:

/etc/ssh/sshd_config

我们根据它的配置文件来学习SSH中的身份登录验证机制

0x1: PasswordAuthentication

这是最常见、普遍的一种SSH登录验证机制,在这种验证模式下,我们在登录SSH服务器的时候,console的回显是提示我们输入用户名、密码,并根据真实性决定是否登录成功,用户名、密码的信
息库来自于Linux自身的账户信息库(/etc/passwd)

0x2: ChallengeResponseAuthentication

顾名思义,挑战身份验证模式,类似于微软的chap协议。openSSH在实现挑战响应模式的时候采用了S/KEY(one-time one-password)
https://wiki.archlinux.org/index.php/S/KEY_Authentication
http://en.wikipedia.org/wiki/S/KEY
在ssh下配置一次一密S/KEY需要安装额外的模块

0x3: RSAAuthentication、PubkeyAuthentication

这就是我们常说的RSA证书登录机制,或者叫免密码登录机制。根据RSA公钥算法的原理,一对RSA证书分为公钥证书、私钥证书。私钥证书保存在客户端,用于证明自己的身份,而公钥证书保存在服
务端,用于加、解密消息 ssh
-client、putty都可以配置RSA证书进行免密码登录 http://blog.chinaunix.net/uid-773723-id-152230.html http://blog.lizhigang.net/archives/249 http://www.360doc.com/content/12/0502/11/9523733_208062914.shtml http://nichael1983.blog.163.com/blog/static/114969433201002711850400/ (配置的过程不复杂,注意权限问题即可)

0x4: KerberosAuthentication

KerberosAuthentication是一种中心式的身份认证机制,在ssh上使用KerberosAuthentication认证,需要安装额外的KerberosAuthentication模块
http://docstore.mik.ua/orelly/networking_2ndEd/ssh/ch11_04.htm
http://blog.scottlowe.org/2006/08/21/native-kerberos-authentication-with-ssh/
http://publib.boulder.ibm.com/infocenter/pseries/v5r3/index.jsp?topic=/com.ibm.aix.security/doc/security/using_openssh_with_kerberosv5.htm

0x5: GSSAPIAuthentication

GSSAPIAuthentication只能在SSH2协议下使用
http://www.slac.stanford.edu/comp/unix/sshGSSAPI.html

0x6: UsePAM

我们之前学习过,PAM(Pluggable Authentication Module)是一种对底层身份认证代码逻辑的API封装,SSH同样也支持PAM认证
UsePAMEnables the Pluggable Authentication Module
interface. If set to ``yes'' this will enable PAM authentication using
ChallengeResponseAuthentication and PAM account and session module processing for all authentication types. Because PAM challenge-response authentication usually serves an equivalent role to password authentication, you should disable either
PasswordAuthentication or ChallengeResponseAuthentication. If UsePAM
is enabled, you will not be able to run sshd(8) as a non-root user. The default is ``no'' http://www.linuxmanpages.com/man5/sshd_config.5.php cat /etc/pam.d/sshd #%PAM-1.0 auth include system-auth account required pam_nologin.so account include system-auth password include system-auth session optional pam_keyinit.so force revoke session include system-auth session required pam_loginuid.so

关于SSH的这几种密码验证机制,我在本地实验的时候总结了一下它们的顺序

1. SSH服务器"开启"了PAM验证: UsePAM=yes
    1) SSH服务器"开启"了交互式密码验证: PasswordAuthentication=yes
        1.1) 证书的RSA私钥错误: 服务器提示进行PAM验证
        1.2) 证书的RSA私钥正确: 登录成功
    2) SSH服务器"关闭"了交互式密码验证: PasswordAuthentication=no
        2.1) 证书的RSA私钥错误: 服务器提示进行PAM验证
        2.2) 证书的RSA私钥正确: 登录成功
2. SSH服务器"关闭"了PAM验证: UsePAM=no
    1) SSH服务器"开启"了交互式密码验证: PasswordAuthentication=yes
        1.1) 证书的RSA私钥错误: 服务器提示进行交互式密码验证
        1.2) 证书的RSA私钥正确: 登录成功
    2) SSH服务器"关闭"了交互式密码验证: PasswordAuthentication=no
        1.1) 证书的RSA私钥错误: 服务器提示登录失败: Permission denied (publickey,gssapi-with-mic,password,keyboard-interactive)
        1.2) 证书的RSA私钥正确: 登录成功

综上来看,可以知道,PAM验证是优先于PasswordAuthentication验证进行的(前提是当证书RSA私钥错误的时候)

 

SSH Bruteforce Login的相关方法

SSH的暴力破解,或者说所有系统的暴力破解都有两个思考方向:

1. 基于字典、词组组合进行正常流程的穷举: 速度较慢、且容易受到爆破锁定防御的影响
2. 从目标开源系统的源代码下手,利用信息泄漏、PRNG伪随机漏洞进行算法攻击

1. 穷举

https://github.com/addthis/hydra
http://projecthydra.org/

2. 算法攻击

http://www.exploit-db.com/exploits/5720/

 

4. Linux PAM Module Code Example

0x1: Sample PAM Application

The following is a minimal implementation of su(1) using PAM. Note that it uses the OpenPAM-specific openpam_ttyconv(3) conversation function, which is prototyped in security/openpam.h. If you wish build this application on a system with a different PAM library, you will have to provide your own conversation function. A robust conversation function is surprisingly difficult to implement; the one presented in Appendix C, Sample PAM Conversation Function is a good starting point, but should not be used in real-world applications.

/*-
 * Copyright (c) 2002,2003 Networks Associates Technology, Inc.
 * All rights reserved.
 *
 * This software was developed for the FreeBSD Project by ThinkSec AS and
 * Network Associates Laboratories, the Security Research Division of
 * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
 * ("CBOSS"), as part of the DARPA CHATS research program.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $P4: //depot/projects/openpam/bin/su/su.c#10 $
 * $FreeBSD: head/en_US.ISO8859-1/articles/pam/su.c 38826 2012-05-17 19:12:14Z hrs $
 */

#include <sys/param.h>
#include <sys/wait.h>

#include <err.h>
#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <unistd.h>

#include <security/pam_appl.h>
#include <security/openpam.h>    /* for openpam_ttyconv() */

extern char **environ;

static pam_handle_t *pamh;
static struct pam_conv pamc;

static void
usage(void)
{

    fprintf(stderr, "Usage: su [login [args]]\n");
    exit(1);
}

int
main(int argc, char *argv[])
{
    char hostname[MAXHOSTNAMELEN];
    const char *user, *tty;
    char **args, **pam_envlist, **pam_env;
    struct passwd *pwd;
    int o, pam_err, status;
    pid_t pid;

    while ((o = getopt(argc, argv, "h")) != -1)
        switch (o) {
        case 'h':
        default:
            usage();
        }

    argc -= optind;
    argv += optind;

    if (argc > 0) {
        user = *argv;
        --argc;
        ++argv;
    } else {
        user = "root";
    }

    /* initialize PAM */
    pamc.conv = &openpam_ttyconv;
    pam_start("su", user, &pamc, &pamh);

    /* set some items */
    gethostname(hostname, sizeof(hostname));
    if ((pam_err = pam_set_item(pamh, PAM_RHOST, hostname)) != PAM_SUCCESS)
        goto pamerr;
    user = getlogin();
    if ((pam_err = pam_set_item(pamh, PAM_RUSER, user)) != PAM_SUCCESS)
        goto pamerr;
    tty = ttyname(STDERR_FILENO);
    if ((pam_err = pam_set_item(pamh, PAM_TTY, tty)) != PAM_SUCCESS)
        goto pamerr;

    /* authenticate the applicant */
    if ((pam_err = pam_authenticate(pamh, 0)) != PAM_SUCCESS)
        goto pamerr;
    if ((pam_err = pam_acct_mgmt(pamh, 0)) == PAM_NEW_AUTHTOK_REQD)
        pam_err = pam_chauthtok(pamh, PAM_CHANGE_EXPIRED_AUTHTOK);
    if (pam_err != PAM_SUCCESS)
        goto pamerr;

    /* establish the requested credentials */
    if ((pam_err = pam_setcred(pamh, PAM_ESTABLISH_CRED)) != PAM_SUCCESS)
        goto pamerr;

    /* authentication succeeded; open a session */
    if ((pam_err = pam_open_session(pamh, 0)) != PAM_SUCCESS)
        goto pamerr;

    /* get mapped user name; PAM may have changed it */
    pam_err = pam_get_item(pamh, PAM_USER, (const void **)&user);
    if (pam_err != PAM_SUCCESS || (pwd = getpwnam(user)) == NULL)
        goto pamerr;

    /* export PAM environment */
    if ((pam_envlist = pam_getenvlist(pamh)) != NULL) {
        for (pam_env = pam_envlist; *pam_env != NULL; ++pam_env) {
            putenv(*pam_env);
            free(*pam_env);
        }
        free(pam_envlist);
    }

    /* build argument list */
    if ((args = calloc(argc + 2, sizeof *args)) == NULL) {
        warn("calloc()");
        goto err;
    }
    *args = pwd->pw_shell;
    memcpy(args + 1, argv, argc * sizeof *args);

    /* fork and exec */
    switch ((pid = fork())) {
    case -1:
        warn("fork()");
        goto err;
    case 0:
        /* child: give up privs and start a shell */

        /* set uid and groups */
        if (initgroups(pwd->pw_name, pwd->pw_gid) == -1) {
            warn("initgroups()");
            _exit(1);
        }
        if (setgid(pwd->pw_gid) == -1) {
            warn("setgid()");
            _exit(1);
        }
        if (setuid(pwd->pw_uid) == -1) {
            warn("setuid()");
            _exit(1);
        }
        execve(*args, args, environ);
        warn("execve()");
        _exit(1);
    default:
        /* parent: wait for child to exit */
        waitpid(pid, &status, 0);

        /* close the session and release PAM resources */
        pam_err = pam_close_session(pamh, 0);
        pam_end(pamh, pam_err);

        exit(WEXITSTATUS(status));
    }

pamerr:
    fprintf(stderr, "Sorry\n");
err:
    pam_end(pamh, pam_err);
    exit(1);
}

0x2: Sample PAM Module

The following is a minimal implementation of pam_unix(8), offering only authentication services. It should build and run with most PAM implementations, but takes advantage of OpenPAM extensions if available: note the use of pam_get_authtok(3), which enormously simplifies prompting the user for a password.

/*-
 * Copyright (c) 2002 Networks Associates Technology, Inc.
 * All rights reserved.
 *
 * This software was developed for the FreeBSD Project by ThinkSec AS and
 * Network Associates Laboratories, the Security Research Division of
 * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
 * ("CBOSS"), as part of the DARPA CHATS research program.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $P4: //depot/projects/openpam/modules/pam_unix/pam_unix.c#3 $
 * $FreeBSD: head/en_US.ISO8859-1/articles/pam/pam_unix.c 38826 2012-05-17 19:12:14Z hrs $
 */

#include <sys/param.h>

#include <pwd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>

#include <security/pam_modules.h>
#include <security/pam_appl.h>

#ifndef _OPENPAM
static char password_prompt[] = "Password:";
#endif

#ifndef PAM_EXTERN
#define PAM_EXTERN
#endif

PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags,
    int argc, const char *argv[])
{
#ifndef _OPENPAM
    struct pam_conv *conv;
    struct pam_message msg;
    const struct pam_message *msgp;
    struct pam_response *resp;
#endif
    struct passwd *pwd;
    const char *user;
    char *crypt_password, *password;
    int pam_err, retry;

    /* identify user */
    if ((pam_err = pam_get_user(pamh, &user, NULL)) != PAM_SUCCESS)
        return (pam_err);
    if ((pwd = getpwnam(user)) == NULL)
        return (PAM_USER_UNKNOWN);

    /* get password */
#ifndef _OPENPAM
    pam_err = pam_get_item(pamh, PAM_CONV, (const void **)&conv);
    if (pam_err != PAM_SUCCESS)
        return (PAM_SYSTEM_ERR);
    msg.msg_style = PAM_PROMPT_ECHO_OFF;
    msg.msg = password_prompt;
    msgp = &msg;
#endif
    for (retry = 0; retry < 3; ++retry) {
#ifdef _OPENPAM
        pam_err = pam_get_authtok(pamh, PAM_AUTHTOK,
            (const char **)&password, NULL);
#else
        resp = NULL;
        pam_err = (*conv->conv)(1, &msgp, &resp, conv->appdata_ptr);
        if (resp != NULL) {
            if (pam_err == PAM_SUCCESS)
                password = resp->resp;
            else
                free(resp->resp);
            free(resp);
        }
#endif
        if (pam_err == PAM_SUCCESS)
            break;
    }
    if (pam_err == PAM_CONV_ERR)
        return (pam_err);
    if (pam_err != PAM_SUCCESS)
        return (PAM_AUTH_ERR);

    /* compare passwords */
    if ((!pwd->pw_passwd[0] && (flags & PAM_DISALLOW_NULL_AUTHTOK)) ||
        (crypt_password = crypt(password, pwd->pw_passwd)) == NULL ||
        strcmp(crypt_password, pwd->pw_passwd) != 0)
        pam_err = PAM_AUTH_ERR;
    else
        pam_err = PAM_SUCCESS;
#ifndef _OPENPAM
    free(password);
#endif
    return (pam_err);
}

PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh, int flags,
    int argc, const char *argv[])
{

    return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
    int argc, const char *argv[])
{

    return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags,
    int argc, const char *argv[])
{

    return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags,
    int argc, const char *argv[])
{

    return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
    int argc, const char *argv[])
{

    return (PAM_SERVICE_ERR);
}

#ifdef PAM_MODULE_ENTRY
PAM_MODULE_ENTRY("pam_unix");
#endif

0x3: Sample PAM Conversation Function

The conversation function presented below is a greatly simplified version of OpenPAM's openpam_ttyconv(3). It is fully functional, and should give the reader a good idea of how a conversation function should behave, but it is far too simple for real-world use. Even if you are not using OpenPAM, feel free to download the source code and adapt openpam_ttyconv(3) to your uses; we believe it to be as robust as a tty-oriented conversation function can reasonably get.

/*-
 * Copyright (c) 2002 Networks Associates Technology, Inc.
 * All rights reserved.
 *
 * This software was developed for the FreeBSD Project by ThinkSec AS and
 * Network Associates Laboratories, the Security Research Division of
 * Network Associates, Inc.  under DARPA/SPAWAR contract N66001-01-C-8035
 * ("CBOSS"), as part of the DARPA CHATS research program.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. The name of the author may not be used to endorse or promote
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 * $FreeBSD: head/en_US.ISO8859-1/articles/pam/converse.c 38826 2012-05-17 19:12:14Z hrs $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include <security/pam_appl.h>

int
converse(int n, const struct pam_message **msg,
    struct pam_response **resp, void *data)
{
    struct pam_response *aresp;
    char buf[PAM_MAX_RESP_SIZE];
    int i;

    data = data;
    if (n <= 0 || n > PAM_MAX_NUM_MSG)
        return (PAM_CONV_ERR);
    if ((aresp = calloc(n, sizeof *aresp)) == NULL)
        return (PAM_BUF_ERR);
    for (i = 0; i < n; ++i) {
        aresp[i].resp_retcode = 0;
        aresp[i].resp = NULL;
        switch (msg[i]->msg_style) {
        case PAM_PROMPT_ECHO_OFF:
            aresp[i].resp = strdup(getpass(msg[i]->msg));
            if (aresp[i].resp == NULL)
                goto fail;
            break;
        case PAM_PROMPT_ECHO_ON:
            fputs(msg[i]->msg, stderr);
            if (fgets(buf, sizeof buf, stdin) == NULL)
                goto fail;
            aresp[i].resp = strdup(buf);
            if (aresp[i].resp == NULL)
                goto fail;
            break;
        case PAM_ERROR_MSG:
            fputs(msg[i]->msg, stderr);
            if (strlen(msg[i]->msg) > 0 &&
                msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
                fputc('\n', stderr);
            break;
        case PAM_TEXT_INFO:
            fputs(msg[i]->msg, stdout);
            if (strlen(msg[i]->msg) > 0 &&
                msg[i]->msg[strlen(msg[i]->msg) - 1] != '\n')
                fputc('\n', stdout);
            break;
        default:
            goto fail;
        }
    }
    *resp = aresp;
    return (PAM_SUCCESS);
 fail:
        for (i = 0; i < n; ++i) {
                if (aresp[i].resp != NULL) {
                        memset(aresp[i].resp, 0, strlen(aresp[i].resp));
                        free(aresp[i].resp);
                }
        }
        memset(aresp, 0, n * sizeof *aresp);
    *resp = NULL;
    return (PAM_CONV_ERR);

Relevant Link:

https://www.freebsd.org/doc/en_US.ISO8859-1/articles/pam/article.html

 

Copyright (c) 2014 LittleHann All rights reserved

 

posted @ 2014-04-13 20:57  郑瀚Andrew  阅读(7239)  评论(1编辑  收藏  举报