PAM 全名為 Pluggable Authentication Module,可用在跨系統的帳號認證整合。有幸從同事那邊得到這邊的資訊,所以來筆記一下。更多 PAM 資訊可自行 Google 一下。在此筆記把玩的成果。

使用情境:

  • 一個提供帳號登入的 Web Service (A系統)
  • 一個提供 POP3/IMAP/SMTP 服務的 Mail Server (B系統)

如果想要提供 B 系統用 A 系統的帳號來做認證時,這時候就可以採用 PAM 來處理,例如在帳密登入流程中,加入 A系統認證流程。(註:此例以 Mail Server 舉例不甚理想,因為 PAM 無法解決 Mail Server 收信的問題,也就是無法提供 user list 讓 Mail Server 驗證帳號是否有效,在此僅用在登入帳密範例)

作法:

  • 實作 pam_yychecker.so 模組,用來認證 A 系統帳號
  • 使用 Dovecot POP3/IMAP 服務架構,增加 pam_yychecker.so 作為帳密認證

實作 pam_yychecker.so 模組:

pam_yychecker.c:(此例為任何帳密進來都 pass,而認證方面應修改 pam_sm_authenticate 函數,把取到的帳密到 A 系統認證)

// 參考 http://www.openpam.org/browser/openpam/trunk/modules/pam_unix/pam_unix.c

$ sudo apt-get install gcc libpam-dev make
$ gcc -shared -lpam -fPIC -Wall pam_yychecker.c -o pam_yychecker.so
$ sudo install pam_yychecker.so /lib/x86_64-linux-gnu/security

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

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

#include <syslog.h>

#ifndef PAM_EXTERN
#define PAM_EXTERN
#endif

PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
    int i;
   const char *user;
   char *password;

   syslog(LOG_DEBUG, "@ yychecker [pam_sm_authenticate]");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);

   /* identify user */
   if (pam_get_user(pamh, &user, NULL) != PAM_SUCCESS)
      return PAM_AUTH_ERR;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_authenticate] get account:[%s]", user);

   /* get password */
   if (pam_get_authtok(pamh, PAM_AUTHTOK, (const char **)&password, NULL) != PAM_SUCCESS)
      return PAM_AUTH_ERR;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_authenticate] get password:[%s]", password);

   //
   //  add some checker
   // ...

   return PAM_SUCCESS;
}

PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_setcred]");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_acct_mgmt]");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_open_session");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_close_session");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SUCCESS);
}

PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
int argc, const char *argv[])
{
   int i;
   syslog(LOG_DEBUG, "@ yychecker [pam_sm_chauthtok]");
   for (i=0 ; i < argc ; i++)
      syslog(LOG_DEBUG, "argv[%d]=[%s]",i, argv[i]);
   return (PAM_SERVICE_ERR);
}

使用 pam_yychecker.so 模組:

在此挑 Dovecot IMAP/POP3 為例:

@ /etc/pam.d/yychecker

auth required pam_checker.so debug  # "debug" 可讓 pam_yychecker.c 中的 syslog(LOG_DEBUG, "" ) 起作用,在 /var/log/syslog 可看見訊息
account required pam_checker.so debug arg1=val1  # arg1=val1 純粹測試用,可在 pam_yychecker.c 看到參數傳進來

@ /etc/dovecot/conf.d/99-yychecker.conf

auth_mechanisms = plain login
protocols = pop3 imap
mail_location = maildir:/home/vmail/%u/Maildir
auth_debug=yes # 更多豐富的訊息
passdb {
    driver = pam
    args = yychecker   # /etc/pam.d/yychecker 
}
userdb {
    driver = static
    args = uid=vmail gid=vmail home=/home/vmail/%u
}

透過撰寫上述兩個檔案後,將 dovecot 重開後 (/etc/init.d/dovecot restart),可以用 telnet/openssl 訪問 pop3/imap 服務:

IMAP:

$ openssl s_client -connect localhost:993
...
* OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE AUTH=PLAIN AUTH=LOGIN] Dovecot ready.
- login account password
- OK [CAPABILITY IMAP4rev1 LITERAL+ SASL-IR LOGIN-REFERRALS ID ENABLE IDLE SORT SORT=DISPLAY THREAD=REFERENCES THREAD=REFS MULTIAPPEND UNSELECT CHILDREN NAMESPACE UIDPLUS LIST-EXTENDED I18NLEVEL=1 CONDSTORE QRESYNC ESEARCH ESORT SEARCHRES WITHIN CONTEXT=SEARCH LIST-STATUS] Logged in
- logout
* BYE Logging out
- OK Logout completed.
closed

POP3:

$ openssl s_client -connect localhost:995
...
+OK Dovecot ready.
user account
+OK
pass password
+OK Logged in.
quit
+OK Logging out.
closed

上述例子藍色是輸入的指令,其中 - login 後面依序接帳號跟密碼,由於在 pam_yychecker.c 之 pam_sm_authenticate 函數中,都是回傳 PAM_SUCCESS ,因此無論哪組(不存在的)帳密都會因此 PASS 的

此外還可以翻一下 /var/log/syslog 得知一下訊息:(此次帳號為test, 密碼為test)

dovecot: auth: Debug: Loading modules from directory: /usr/lib/dovecot/modules/auth
ovecot: auth: Debug: auth client connected (pid=14779)
dovecot: auth: Debug: client in: AUTH#0111#011PLAIN#011service=pop3#011secured#011lip=127.0.0.1#011rip=127.0.0.1#011lport=995#011rport=44325#011resp=AHRlc3QAdGVzdA==
dovecot: auth-worker: Debug: Loading modules from directory: /usr/lib/dovecot/modules/auth
dovecot: auth-worker: Debug: pam(test,127.0.0.1): lookup service=dovecot
dovecot: auth-worker: Debug: pam(test,127.0.0.1): #1/1 style=1 msg=Password:
dovecot: auth-worker: pam(test,127.0.0.1): pam_authenticate() failed: Authentication failure (password mismatch?) (given password: test)
dovecot: auth-worker: Debug: pam(test,127.0.0.1): lookup service=yychecker
auth: @ yychecker [pam_sm_authenticate]
auth: argv[0]=[debug]
auth: @ yychecker [pam_sm_authenticate] get account:[test]
dovecot: auth-worker: Debug: pam(test,127.0.0.1): #1/1 style=1 msg=Password:
auth: @ yychecker [pam_sm_authenticate] get password:[test]
auth: @ yychecker [pam_sm_acct_mgmt]
auth: argv[0]=[debug]
auth: argv[1]=[arg1=val1]
dovecot: auth: Debug: client out: OK#0111#011user=test
dovecot: auth: Debug: master in: REQUEST#0112190999553#01114779#0111#011a7a91b0fc5002a5eb77113ceb9304c7c
dovecot: auth: Debug: passwd(test,127.0.0.1): lookup
dovecot: auth: passwd(test,127.0.0.1): unknown user
dovecot: auth: Debug: master out: USER#0112190999553#011test#011uid=1002#011gid=1003#011home=/home/vmail/test
dovecot: pop3-login: Login: user=<test>, method=PLAIN, rip=127.0.0.1, lip=127.0.0.1, mpid=14783, TLS
dovecot: pop3(test): Disconnected: Logged out top=0/0, retr=0/0, del=0/0, size=0

從 syslog 大概可以看到,認證流程一開始是內建的帳密系統(/etc/pam.d/dovecot敘述),後來接著走 (/etc/pam.d/yychecker),接著依序走過 pam_sm_authenticate 和 pam_sm_acct_mgmt 函數。如果未實作 pam_sm_acct_mgmt 或 pam_sm_acct_mgmt 回傳 PAM_AUTH_ERR 時,也看到認證失敗的訊息。因此,對於 dovecot PAM 的使用中,除了一開始在 pam_sm_authenticate 就處理帳號過濾外,也可在 pam_sm_acct_mgmt 進行帳號的過濾。

原先是打算利用 PAM 來提供 Mail server (B系統) 使用 Web service (A系統) 帳號進行收信、送信,但測試的結果,盡管可以讓 POP3/IMAP/SMTP 認證都搞定(SMTP還須額外設定,上述範例設定檔還不夠),但在 Mail Server 之 deliver 上,採用 Dovecot LDA 卻碰到 userdb static 問題,也就是無法提供帳號認證,導致 Mail server 無把將 A 系統帳號視為有效帳號。也有可能是 PAM 寫錯或調教經驗上淺,玩了兩天還沒有好的成果。目前的結論是...還是乖乖用 dovecot userdb 其他要製造 user list 清單的方式吧 :P 在此就先筆記這幾天亂玩的東西。


, , , , , , ,

changyy 發表在 痞客邦 PIXNET 留言(0) 人氣()