Программа получает PAM_PERM_DENIED (7) при аутентификации в Active Directory, в то время как ssh работает

Я использую Active Domain jamie_ad1.net, и у меня там есть пользователь greg. Когда я ssh -l greg@jamie_ad1.net x.x.x.x успешно вхожу в систему. Однако, когда я аутентифицирую пользователя в своей программе, моя функция process_user() (ниже) возвращает ошибку 7 (PAM_PERM_DENIED — вызывающая сторона не обладает требуемыми полномочиями.)

Что я делаю неправильно? Обратите внимание, что во время ssh используется /etc/pam.d/system-auth. Я также использую то же имя службы PAM "system_auth" в своей программе (и я также использовал «sshd» и «логин» - все не удалось.)

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() возвращает УСПЕХ, поэтому он подтверждает существование пользователя 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 auth required pam_sepermit.so auth include password-auth account required pam_nologin.so account include password-auth password include password-auth

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

person Grzegorz    schedule 21.07.2016    source источник
comment
Откуда вы знаете, что ssh использует /etc/pam.d/system-auth и ничего больше? Обычно я ожидаю, что sshd будет использовать свою собственную конфигурацию службы (/etc/pam.d/sshd), которая будет включать эту общую конфигурацию и добавлять свою конфигурацию службы...   -  person Peter Brittain    schedule 02.08.2016
comment
@PeterBrittain: Спасибо за интерес. Это связано с тем, что sshd, логин и т. д. включают системную аутентификацию или парольную аутентификацию. В моем случае оба файла одинаковы. Я также упомянул, что я также звонил pam_start( "sshd" ...), и результат был таким же.   -  person Grzegorz    schedule 02.08.2016
comment
Клиент SSH может использовать предварительно настроенную инфраструктуру Kerberos для входа в Active Directory. Проверьте /etc/krb5.conf, чтобы узнать, есть ли у него конфигурация для домена jamie_ad1.net. Или же запустите klist сразу после успешного сеанса ssh и проверьте, отображаются ли активные билеты Kerberos. В этом случае вам может понадобиться научиться использовать pam_krb5.   -  person void    schedule 02.08.2016
comment
@void: /etc/krb5.conf содержит правильную конфигурацию. Однако klist показывает это: klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_16777216) - BTW /tmp/krb5cc_16777216 не существует.   -  person Grzegorz    schedule 02.08.2016
comment
@void: я заметил, что там был устаревший клист, которого там быть не должно. Итак, после его удаления и повторной инициализации всего с нуля я получаю этот вывод от klist после успешного ssh-ing (я ставлю lf› в конце каждой строки, чтобы упростить ее восстановление:) Ticket cache: FILE:/tmp/krb5cc_16777216‹lf› Default principal: grg@JAMIE_AD1.NET ‹lf› ‹lf› Valid starting Expires Service principal‹lf› 08/02/16 15:09:59 08/03/16 01:10:05 krbtgt/JAMIE_AD1.NET@JAMIE_AD1.NET‹lf› renew until 08/09/16 15:09:59‹lf›   -  person Grzegorz    schedule 03.08.2016
comment
@Grzegorz: Ваше наблюдение о том, что вызов pam_acct_mgmt() возвращает УСПЕХ, может означать, что по крайней мере часть подсистемы PAM настроена на использование Kerberos. Есть ли какие-либо файлы в разделе /etc/pam.d/, которые ссылаются на pam_krb5.so?   -  person void    schedule 03.08.2016
comment
@void: ни у кого нет ссылок на этот или любой другой модуль, содержащий «krb» в имени. Только винбинд. Я обновлю тикет, чтобы показать, что содержимое /etc/pam.d/system-auth и /etc/pam.d/sshd', and annotation that password-auth` совпадает с system-auth   -  person Grzegorz    schedule 03.08.2016
comment
Как вы определили, что pam_acct_mgmt() возвращает успех? В коде, который вы разместили, он никогда не будет вызываться, если pam_authenticate() завершится ошибкой.   -  person nephtes    schedule 03.08.2016
comment
@nephtes: потому что я уже изменил код. Я исправлю пример, так что это не будет препятствием для всех. Спасибо Вам за Ваш вопрос.   -  person Grzegorz    schedule 03.08.2016


Ответы (2)


Вход с помощью SSH-клиента работает, поскольку он не использует подсистему PAM для аутентификации на внешнем хосте AD. Клиент SSH проверяет флаг GSSAPIAuthentication в своей конфигурации (/etc/ssh/ssh_config или /etc/ssh_config или ~/.ssh/config) и вызывает библиотеку GSSAPI. GSSAPI использует Kerberos, который установлен и правильно настроен на хосте Linux, на котором работает ваша программа.

Если целью программы является аутентификация учетных данных пользователя в Active Directory, она должна сделать то же самое и вызвать GSSAPI. Альтернативным способом (возможно, более простым в использовании) может быть аутентификация через LDAP API. . Просто обратите внимание, что установки AD иногда отвергают простую аутентификацию открытым текстом по незашифрованному LDAP и требуют аутентификации на основе SASL или SSL-соединения.

Вызов pam_start( "system-auth", в вашем коде означает «аутентифицировать пользователя так же, как это делают другие службы на основе PAM на этом хосте Linux». Чтобы это заработало, необходимо связаться с администратором хоста Linux (тот, кто настроил клиент Kerberos) и попросить его/его выполнить интеграция с Active Directory. Результатом успешной настройки будет то, что службы на основе PAM (такие как login, su, sshd и т. д.) начнут понимать учетные данные AD, а значит, и ваш код. Этот подход также имеет свои ограничения:

  • ваша программа всегда будет зависеть от рабочей настройки интеграции AD хоста, на котором она работает, т. е. правильной конфигурации Kerberos, PAM и/или Samba,
  • «system-auth» — это имя службы PAM, специфичное для дистрибутивов на основе RedHat, оно может отсутствовать в системах на основе Debian.

Если коду требуется только аутентификация в AD с помощью подсистемы PAM и использование службы «системной аутентификации» не является обязательным, может помочь следующая минимальная конфигурация. Опять же, без квалифицированного администратора Linux это было бы довольно рискованным предприятием:

  1. Установите пакет pam_krb5 в Red Hat, libpam-krb5 в Debian.
  2. Найдите точное местоположение pam_krb5.so, на моем компьютере с Ubuntu это было /lib/x86_64-linux-gnu/security/pam_krb5.so.
  3. Создайте новый файл описания службы /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", и скрестить пальцы.
person void    schedule 03.08.2016
comment
Дорогая пустота: Спасибо за ответ. Я не уверен, выведет ли это меня из леса, но ваш ответ кажется разумным, и, воодушевленный двумя голосами, я дам вам то, что вам причитается. Я дам вам знать где-нибудь в будущем, если я заставлю его работать с вашей помощью. Еще раз спасибо! - person Grzegorz; 04.08.2016
comment
@Grzegorz: Спасибо за награду. Мне жаль, что я не смог полностью решить вашу проблему, но это связано с двумя основными препятствиями: 1) неясно, какую именно функцию вы пытаетесь реализовать, 2) подход на основе PAM требует навыков администратора, которые мы оба, кажется, не обладают (я никогда не видел Linux-бокс, интегрированный в среду AD до уровня PAM). - person void; 05.08.2016
comment
@Grzegorz: Тем не менее, есть некоторые дополнительные подсказки, которые могут помочь с приведенным выше фрагментом кода: вы можете прекратить попытки настроить службу «системная аутентификация» и создать специальную службу PAM, которая используется только для ваших целей. - person void; 05.08.2016
comment
Спасибо за издание и помощь! Ваш пост помог убедиться, что это выполнимо, и что проблема в коде. Я добавлю новый ответ, но я оставлю ваш ответ как решение, для которого вы приложили много усилий, и оно определенно поможет другим, ищущим ответ. - person Grzegorz; 06.08.2016

Во-первых, спасибо пользователю void за его помощь и нелегкий труд.

В итоге проблема оказалась в библиотеке в наборе локальных библиотек, которые были связаны с конечным продуктом. Примечание. Это не что-то из системы. Это локальная библиотека, которая больше не нужна, но перезаписывает некоторые функции PAM, необходимые для Active Directory (например, LDAP работал нормально). пример до минимума, как показано ниже (я должен был сделать это с самого начала!)

Приведенный выше пример при компиляции с использованием команды gcc example.c -o example -lpam будет работать отлично. Проблема с этим примером заключалась в том, что я компилировал его в системе, которая выводила множество других библиотек.

После удаления «плохой» библиотеки из списка библиотек (на это ушло некоторое время...) конечный продукт работает хорошо, и вход в Active Directory проходит нормально.

Спасибо всем, кто потратил некоторое время на его изучение.

person Grzegorz    schedule 06.08.2016