1
votes

I am using an Active Domain jamie_ad1.net, and I have a user greg there. When I ssh -l greg@jamie_ad1.net x.x.x.x I am successfully being logged in. When, however, I am authenticating the user in my program, my process_user() function (below) returns error 7 (PAM_PERM_DENIED - The caller does not possess the required authority.)

What do I do wrong? Note that during ssh, it is /etc/pam.d/system-auth that is being used. I am using the same PAM service name "system_auth" in my program, too (and I also used "sshd", and "login" - all failed.)

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 ) ;

   ...
}

Below I am showing the convesation funcion used by process_user() While under gdb I see that num_msg is 1, and the msg[ 0 ]->msg is "Password: ", and that the function properly sets p[ 0 ].resp as strduped password (I have checked that the password is correct.)

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 ;
}

NOTE: The pam_acct_mgmt() call returns SUCCESS, so it confirms that the greg@jamie_ad1.net user exists. It is pam_authenticate() that complains.

Contents of /etc/pam.d/system-auth, which is exactly the same as `/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

Contents of /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
2
How do you know ssh is using /etc/pam.d/system-auth and nothing else? I would typically expect sshd to use its own service configuration (/etc/pam.d/sshd) which would include this common configuration and add its service specific config... - Peter Brittain
@PeterBrittain: Thank you for the interest. It is because sshd, login, etc, include system-auth or password-auth. In my case both files are the same. I have also mentioned that I called pam_start( "sshd" ...) as well, and the result was the same. - Grzegorz
SSH client might use preconfigured Kerberos infrastructure to log into the Active Directory. Examine /etc/krb5.conf to see if it has configuration for domain jamie_ad1.net. Or alternatively run klist right after successful ssh session and check if it displays active Kerberos tickets. In that case you might need to learn using pam_krb5. - void
@void: /etc/krb5.conf contains proper configuration. However klist shows this: klist: No credentials cache found (ticket cache FILE:/tmp/krb5cc_16777216) - BTW /tmp/krb5cc_16777216 does not exist. - Grzegorz
@void: I have noticed that there was lingering klist that should not be there. So after removing it, and re-initializing everything from scratch, I am getting this output from klist after successfully ssh-ing (I am putting <lf> at the end of each line to make it easier to reconstruct it:) 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> - Grzegorz

2 Answers

4
votes

Logging in with SSH client works because it does not use PAM subsystem for authentication on the external AD host. SSH client checks GSSAPIAuthentication flag in its config (/etc/ssh/ssh_config or /etc/ssh_config or ~/.ssh/config) and calls GSSAPI library. GSSAPI uses Kerberos which is installed and correctly configured on the Linux host that your program runs on.

If the program purpose is to authenticate user credentials against Active Directory then it should do the same and call GSSAPI. An alternative way (possibly easier to use) could be authenticating over LDAP API. Just note that AD installations sometimes reject simple plaintext authentication over unencrypted LDAP and require SASL-based authentication or SSL connection.

Call to pam_start( "system-auth", in your code means "authenticate user in the same way as other PAM-based services on this Linux host do". In order to make it work one should contact Linux host administrator (that one who configured Kerberos client) and ask her/him to complete integration with the Active Directory. The result of successful setup would be that PAM-based services (like login, su, sshd and so on) will start understanding AD credentials and so will do your code. This approach also has its limitations:

  • your program will always depend on working AD integration setup of the host it runs on, i.e. proper Kerberos, PAM and/or Samba configuration,
  • "system-auth" is a name of PAM service specific to RedHat-based distributions, it might be missing on Debian-based systems.

If the code only needs authenticate to AD with PAM subsystem and using "system-auth" service is not a requirement then the following minimal configuration might help. Again, without qualified Linux admin it would be quite a risky venture:

  1. Install pam_krb5 package on Red Hat, libpam-krb5 on Debian.
  2. Find exact location of pam_krb5.so, on my Ubuntu box it was /lib/x86_64-linux-gnu/security/pam_krb5.so.
  3. Create a new service description file /etc/pam.d/krb5auth with the following contents (copied from the man page):
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. Test the new service, I would recommend a quick Python session with python-pam. Enter user name either as 'greg' or as 'greg@JAMIE_AD1.NET', uppercase is important for Kerberos sometimes.
  2. Change the pam_start() call in your code to use pam_start( "krb5auth", and cross your fingers.
0
votes

First, thanks to user void for his help and hard work.

At the end the problem was ended up in the library in the set of local libraries that were linked to the final product. Note: It is not something from the system. It is a local library that was not needed anymore but was overwriting some of the PAM functions needed for Active Directory (LDAP, for example, worked okay.) It is user void that gave me the courage to test configuration, and then try to limit the example to the bare minimum, as shown below (I should have done it from the beginning!)

The example above, when compiled using command gcc example.c -o example -lpam will work perfectly fine. The problem I had with this example was that I was compiling it within the system that was pulling into output a lot of other libraries.

After removing the 'bad' library from the list of libraries (it took some time to do that...) the final product works well, and logins into Active Directory fine.

Thank you for all of you who spent some time looking into it.