当针对 Active Directory 进行身份验证时,程序正在接收 PAM_PERM_DENIED (7),而 ssh 有效

Program is rceiving PAM_PERM_DENIED (7) when authenticating against Active Directory, while ssh works

我正在使用一个活动域 jamie_ad1.net,我在那里有一个用户 greg。 当我 ssh -l greg@jamie_ad1.net x.x.x.x 我成功登录。 然而,当我在我的程序中验证用户时,我的 process_user() 函数(如下)returns 错误 7(PAM_PERM_DENIED - 调用者不具备所需的权限。)

我做错了什么?请注意,在 ssh 期间,使用的是 /etc/pam.d/system-auth。我也在我的程序中使用相同的 PAM 服务名称 "system_auth"(我还使用了 "sshd" 和 "login" - 都失败了。)

static int process_user( const char* uname, const char* pwd )
{
    int             rv = 0 ;
    struct pam_conv conv ;
    pam_handle_t*   pamh = NULL;

    conv.conv = &pamauth_conv ;
    conv.appdata_ptr = ( void* )pwd ;

    if (
        (( rv = pam_start( "system-auth", uname, &conv, &pamh )) == PAM_SUCCESS )
        && (( rv = pam_acct_mgmt(pamh, PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK )) == PAM_SUCCESS )
        && (( rv = pam_authenticate( pamh, PAM_SILENT | PAM_DISALLOW_NULL_AUTHTOK )) == PAM_SUCCESS )
       )
        ...

   pam_end( pamh, rv ) ;

   ...
}

下面我展示了process_user()使用的对话函数 在 gdb 下,我看到 num_msg 是 1,msg[ 0 ]->msg"Password: ",并且该函数正确地将 p[ 0 ].resp 设置为 strduped 密码(我已经检查过密码正确。)

static int pamauth_conv( int num_msg, const struct pam_message** msg, struct pam_response** resp, void* appdata_ptr )
{
   int                  rv = PAM_SUCCESS ;
   struct pam_response* p = NULL ;
   int                  i ;

   p = calloc( num_msg, sizeof( struct pam_response )) ;
   if ( p == NULL )
      rv = PAM_BUF_ERR ;
   else
   {
      for ( i = 0; ( rv == PAM_SUCCESS ) && ( i < num_msg ); i++ )
         if ( strcmp( msg[ i ]->msg, "Password: " ) == 0 ) /* support password conversation only */
         {
            p[ i ].resp = strdup(( char* )appdata_ptr ) ;
            if ( p[ i ].resp == NULL )
               rv = PAM_BUF_ERR;
         }
   }

   if ( rv == PAM_SUCCESS )
      *resp = p ;
   else if ( p )
   {
      for ( i = 0; i < num_msg; i++ )
         if ( p[ i ].resp )
            free( p[ i ].resp ) ;

      free( p ) ;
   }

   return rv ;
}

注意: pam_acct_mgmt() 调用 returns 成功,因此确认 greg@jamie_ad1.net 用户存在。抱怨的是pam_authenticate()

/etc/pam.d/system-auth的内容,与`/etc/pam.d/password-auth'完全相同:

#%PAM-1.0
# This file is auto-generated.
# User changes will be destroyed the next time authconfig is run.
auth        required      pam_env.so
auth        sufficient    pam_fprintd.so
auth        sufficient    pam_unix.so nullok try_first_pass
auth        requisite     pam_succeed_if.so uid >= 500 quiet
auth        sufficient    pam_ldap.so use_first_pass
auth        sufficient    pam_winbind.so use_first_pass
auth        required      pam_deny.so

account     required      pam_unix.so broken_shadow
account     sufficient    pam_localuser.so
account     sufficient    pam_succeed_if.so uid < 500 quiet
account     [default=bad success=ok user_unknown=ignore] pam_ldap.so
account     [default=bad success=ok user_unknown=ignore] pam_winbind.so
account     required      pam_permit.so

password    requisite     pam_cracklib.so try_first_pass retry=3 type=
password    sufficient    pam_unix.so sha512 shadow nullok try_first_pass use_authtok
password    sufficient    pam_ldap.so use_authtok
password    sufficient    pam_winbind.so use_authtok
password    required      pam_deny.so

session     optional      pam_keyinit.so revoke
session     required      pam_limits.so
session     optional      pam_oddjob_mkhomedir.so umask=0077
session     [success=1 default=ignore] pam_succeed_if.so service in crond quiet use_uid
session     required      pam_unix.so
session     optional      pam_ldap.so

/etc/pam.d/sshd的内容 #%PAM-1.0 需要身份验证 pam_sepermit.so 身份验证包括密码验证 需要帐户 pam_nologin.so 帐户包括密码验证 密码包括密码验证

session    required     pam_selinux.so close
session    required     pam_loginuid.so
session    required     pam_selinux.so open env_params
session    optional     pam_keyinit.so force revoke
session    include      password-auth
session required pam_lastlog.so showfailed

使用 SSH 客户端登录是可行的,因为它不使用 PAM 子系统在外部 AD 主机上进行身份验证。 SSH 客户端检查其配置中的 GSSAPIAuthentication 标志(/etc/ssh/ssh_config/etc/ssh_config~/.ssh/config)并调用 GSSAPI 库。 GSSAPI 使用 Kerberos,该 Kerberos 安装并正确配置在运行程序的 Linux 主机上。

如果程序的目的是根据 Active Directory 验证用户凭据,那么它应该做同样的事情并调用 GSSAPI. An alternative way (possibly easier to use) could be authenticating over LDAP API。请注意,AD 安装有时会拒绝通过未加密的 LDAP 进行简单的明文身份验证,并需要基于 SASL 的身份验证或 SSL 连接。

在您的代码中调用 pam_start( "system-auth", 意味着 "authenticate user in the same way as other PAM-based services on this Linux host do"。为了使其工作,应该联系 Linux 主机管理员(配置 Kerberos 客户端的那个人)并要求 her/him 完成 integration with the Active Directory。成功设置的结果是基于 PAM 的服务(如登录、su、sshd 等)将开始理解 AD 凭据,因此将执行您的代码。这种方法也有其局限性:

  • 您的程序将始终依赖于运行它的主机的有效 AD 集成设置,即正确的 Kerberos、PAM and/or Samba 配置,
  • "system-auth" 是特定于基于 RedHat 的发行版的 PAM 服务名称,它可能在基于 Debian 的系统上缺失。

如果代码只需要使用 PAM 子系统向 AD 进行身份验证并且不需要使用 "system-auth" 服务,那么以下最小配置可能会有所帮助。同样,如果没有合格的 Linux 管理员,这将是一个非常冒险的冒险:

  1. 在 Red Hat 上安装 pam_krb5 软件包,在 Debian 上安装 libpam-krb5。
  2. 在我的 Ubuntu 盒子上找到 pam_krb5.so 的确切位置 /lib/x86_64-linux-gnu/security/pam_krb5.so.
  3. 使用以下内容(从 man page 复制)创建一个新的服务描述文件 /etc/pam.d/krb5auth
auth            sufficient      /lib/x86_64-linux-gnu/security/pam_krb5.so minimum_uid=1000
session         required        /lib/x86_64-linux-gnu/security/pam_krb5.so minimum_uid=1000
account         required        /lib/x86_64-linux-gnu/security/pam_krb5.so minimum_uid=1000
password        sufficient      /lib/x86_64-linux-gnu/security/pam_krb5.so minimum_uid=1000
  1. 测试新服务,我会推荐一个快速的 Python 会话 python-pam。输入用户名 'greg' 或 'greg@JAMIE_AD1.NET',有时大写对于 Kerberos 很重要。
  2. 更改代码中的 pam_start() 调用以使用 pam_start( "krb5auth", 并祈祷。

首先感谢用户void的帮助和辛勤工作。

最后,问题在链接到最终产品的一组本地库中的库中结束。注意:它是不是来自系统的东西。它是一个不再需要的本地库,但覆盖了 Active Directory 所需的一些 PAM 功能(例如,LDAP,工作正常。)是用户无效给了我测试配置的勇气,然后尝试限制示例到最低限度,如下所示(我应该从一开始就这样做!)

上面的示例在使用命令 gcc example.c -o example -lpam 编译时将工作得很好。我在这个例子中遇到的问题是我在系统中编译它,这个系统正在输出很多其他库。

从库列表中删除 'bad' 库后(这花了一些时间...)最终产品运行良好,并且可以正常登录到 Active Directory。

感谢所有花时间研究它的人。