Linux登入驗證機制、SSH Bruteforce Login學習

Andrew.Hann發表於2014-04-13

相關學習資料

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

 

相關文章