Page 1 of 1

PAM - (Pluggable Authentication Modules)

Posted: Mon Jul 11, 2011 10:16 am
by auser
I'm trying to get PAM working with PB but for some reason it does not accept my PW.
This is what I've done so far:

My OS: Debian Squeezy 64bit

Created a testuser with a password.
Created a file called "/etc/pam.d/pb_test":

Code: Select all

# On Debian I would prefer following:
#@include common-auth
#@include common-account
#@include common-session
#@include common-password

# Else for testing this should do:
auth       required     pam_unix.so
account    required     pam_unix.so

And my PB-code so far:

Code: Select all

If Not OpenConsole()
  End 1
EndIf

Structure pam_message
  msg_style.i
  msgPtr.i
EndStructure

Structure pam_response
  resp.i
  resp_retcode.i
EndStructure

Global *currentPW

Prototype ProtoPamStart(servicenamePtr, usernamePtr, pamconvPtr, getpamhandlePtr)
Prototype ProtoPamAuthenticate(pamhandlePtr,flag)
Prototype ProtoPamEnd(pamhandlePtr,flag)
Prototype ProtoPamAcctMgmt(pamhandlePtr,flag)
Prototype ProtoPamStrerror(pamhandlePtr,errnum)
If OpenLibrary(0, "libpam.so")
  Global pam_start.ProtoPamStart = GetFunction(0, "pam_start")
  Global pam_authenticate.ProtoPamAuthenticate = GetFunction(0, "pam_authenticate")
  Global pam_end.ProtoPamEnd = GetFunction(0, "pam_end")
  Global pam_acct_mgmt.ProtoPamAcctMgmt = GetFunction(0, "pam_acct_mgmt")
  Global pam_strerror.ProtoPamStrerror = GetFunction(0, "pam_strerror")
  CloseLibrary(0)
  If Not pam_start Or Not pam_authenticate Or Not pam_end Or Not pam_acct_mgmt Or Not pam_strerror
    PrintN("Could NOT load function!")
    End
  EndIf

Else
  PrintN("Could NOT open library!")
  End
EndIf

Procedure Conv(*num_msg,*pam_msg.pam_message,*pam_resp.pam_response,*appdataPtr)
  *resp.pam_response = AllocateMemory(SizeOf(*pam_resp))
  *msg.pam_message = AllocateMemory(SizeOf(*pam_msg))
  Select PeekI(*pam_msg\msg_style.i)
    Case 1 ; Means Echo off
      *resp\resp.i = *currentPW
      *resp\resp_retcode = 0
    Case 2 ; Means Echo on
      PrintN("Echo ON - I guess we should prompt for username here")
  EndSelect
  *pam_resp = *resp
  ProcedureReturn(0)
EndProcedure


PrintN("Authenticating ...")

*pam_conv_callback = @Conv()

retval = pam_start(@"pb_test",@"testuser",@*pam_conv_callback,@pamhandlePtr)
PrintN("Pam start: "+PeekS(pam_strerror(pamhandlePtr,retval)))
*currentPW = @"abc"
retval = pam_authenticate(pamhandlePtr,#NULL);32768)
PrintN("Pam auth: "+PeekS(pam_strerror(pamhandlePtr,retval)))
retval = pam_end(pamhandlePtr,0)
PrintN("Pam end: "+PeekS(pam_strerror(pamhandlePtr,retval)))

CloseConsole()
; ExecutableFormat = Console
I get success at pam_start and it even use my callbackfuntion. But it does not accept my PW. :?
It logs: "purebasic.out: pam_unix(pb_test:auth): auth could not identify password for [testuser]" in /var/log/auth.log


I'm pretty sure my Conv() function is not finished right now. I've searched in more then one other C-code how they did it and all of them are allocating some memory in one or another way and use a "for loop" for incoming *num_msg for the structure. I've no clue how I can do this in PB (it seems some kind of dynamic array at structures). But I think that is currently not the problem (or it would never be) cause the *num_msg is "1" at the current call anyway (so I guess I can skip the loop at this step).




C-Code example that needs some tweaking in PB:

Code: Select all

resp = malloc (net_msgs * sizeof(struct pam_response));
for (i = 0; i < net_msgs; i++) {
  resp[i].resp_retcode = 0;


PB-Code??

Code: Select all

*resp.pam_response = AllocateMemory(SizeOf(*pam_resp))
For i = 0 to net_msg
  resp[i]\resp_retcode = 0   ; that [i] for sure does not work
Next
I would be happy for any helpfull idea. :)

Edit: Fixed small stuff. Even tried already when changing to pam_permit.so for sure I even get sucess with pam_authenticate. But that's not valid cause that always works. ;)

Greetings,
auser

Re: PAM - (Pluggable Authentication Modules)

Posted: Tue Jul 12, 2011 6:58 pm
by idle
The loop is just so you can read and write an array.

Code: Select all

Procedure Conv(*num_msg,*pam_msg.pam_message,*pam_resp.pam_response,*appdataPtr)
  *resp.pam_response = AllocateMemory(SizeOf(pam_response)* *num_msg)
  *msg.pam_message = AllocateMemory(SizeOf(pam_message))
  Select PeekI(*pam_msg\msg_style.i)
    Case 1 ; Means Echo off
      For i = 0 To net_msg
        *resp\resp.i = *currentPW
        *resp\resp_retcode = 0   
        *resp + SizeOf(pam_response)   ;step through 
      Next  
      
    Case 2 ; Means Echo on
      PrintN("Echo ON - I guess we should prompt for username here")
  EndSelect
   
  *pam_resp = *resp
  ProcedureReturn(0)
EndProcedure

Re: PAM - (Pluggable Authentication Modules)

Posted: Wed Jul 13, 2011 8:38 am
by auser
Thanks idle, I've found that way already (and even tried some different) but all that doesn't really solve the problem.
I'm not sure if the structure is really the problem. Currently I guess it's the pointing (or pointing to pointer) or some wrong type.


Anyway I've fumbled around a sollution so far but it's not in PB but in C. I would still prefer a all in one sollution in PB so if anybody can tell me what's wrong with my PB code... :wink:


I'm not that C-Geek - so if somebody see a failure please tell me. But for me it seems to work so far. In my opinion it's nearly the same as I tried already in PB.

Code: Select all

#include <security/pam_appl.h>
#include <security/pam_misc.h>
#include <security/_pam_types.h>

char * pw;

int p_conv(int num_msgs, const struct pam_message **pam_msg, struct pam_response **resp, void *appdata)
{
  int i;
  extern char * pw;
  struct pam_response *resp_tmp = 0;

  resp_tmp = malloc(num_msgs * sizeof(struct pam_response));
  if (!resp_tmp)
    return PAM_CONV_ERR;

  for (i = 0; i < num_msgs; i++) {
    resp_tmp[i].resp_retcode = 0; // not used - so always 0

    switch ((*pam_msg)[i].msg_style) {
      case PAM_PROMPT_ECHO_OFF:
        resp_tmp[i].resp = strdup(pw);
        break;
      default:
        free(resp_tmp);
        return PAM_CONV_ERR;
    }
  }
  *resp = resp_tmp;
  return PAM_SUCCESS;
}


int convwrap(char* user, char* userpw, char* service)
{
   int retcode;
   extern char * pw;
   pw = userpw;
   static struct pam_conv pam_conversation = { p_conv, NULL };
   pam_handle_t * pamh;

   retcode = pam_start(service, user, &pam_conversation, &pamh);

   if (retcode == PAM_SUCCESS) {
      retcode = pam_authenticate(pamh, 0);
   }

   if (retcode == PAM_SUCCESS) {
      retcode = pam_acct_mgmt(pamh, 0);
   }

   // both following session calls can be skipped if just auth is needed
   if (retcode == PAM_SUCCESS) {
      retcode = pam_open_session(pamh, 0);
   }

   if (retcode == PAM_SUCCESS) {
      retcode = pam_close_session(pamh, 0);
   }

   pam_end(pamh, retcode);

   return retcode;
}


To get a shared lib I can use in PB I've used following in a nearly empty folder (that just contains the above code in a pbconvwrap.c file):

Code: Select all

gcc -Wall -fPIC -lpam -lpam_misc -c *.c && \
gcc -shared -lpam -lpam_misc -Wl,-soname,libpbconvwrap.so.1 -o libpbconvwrap.so.1.0 *.o && \
mv libpbconvwrap.so.1.0 /usr/local/lib && \
ln -s /usr/local/lib/libpbconvwrap.so.1.0 /usr/local/lib/libpbconvwrap.so.1 && \
ln -s /usr/local/lib/libpbconvwrap.so.1.0 /usr/local/lib/libpbconvwrap.so || \
echo "Error while building..."

And finaly the (slimmer) PB code:

Code: Select all

#PAM_SUCCESS = 0

If Not OpenConsole()
  End 1
EndIf

Prototype ProtoPamAuth(*user, *pw, *service)
If OpenLibrary(0, "/usr/local/lib/libpbconvwrap.so")
  Global pam_auth.ProtoPamAuth = GetFunction(0, "convwrap")
  CloseLibrary(0)
  If Not pam_auth
    PrintN("Could NOT load function!")
    End
  EndIf

Else
  PrintN("Could NOT open library!")
  End
EndIf

PrintN("Start Authenticating ...")

username.s = "testuser"
password.s = "abc"
servicename.s = "pb_test"


test = pam_auth(@username.s,@password.s,@servicename.s)
if test = #PAM_SUCCESS
  PrintN("Success")
else
  PrintN("Auth Error")
endif

CloseConsole()
; ExecutableFormat = Console

Why is that PAM stuff usefull at all? Cause you can auth via different methods (including LDAP which can do the SSL stuff independend itself setup once for the OS, ...) via one interface. You can even specify groups or excludes in the /etc/pam.d/* file (auth required pam_listfile.so file=/somefilename item=group sense=allow onerr=fail) or create a homedir on the fly if some user logins first (session required pam_mkhomedir.so). So I prefered that one over a direct ldap conversation.


Greetings,
auser

Re: PAM - (Pluggable Authentication Modules)

Posted: Wed Jul 13, 2011 7:59 pm
by idle
That could be useful.

I just noticed that the function is declared with pointer to pointer

Code: Select all

p_conv(int num_msgs, const struct pam_message **pam_msg, struct pam_response **resp, void *appdata)
This might work for PB.

Code: Select all

 
 procedure p_conv(num_msgs.i,*msg.i,*resp.i, void *appdata)
 protected *PamMsg.pam_message 
 protected *PamResp.pam_Response
 *PamMsg = peeki(*msg)
 *PamResp = peeki(*resp) 

Re: PAM - (Pluggable Authentication Modules)

Posted: Thu Jul 14, 2011 4:08 pm
by auser
Hello idle,

I've tried already some stuff via PeekI (and PokeI). It works with *mgs (I peek a valid value). But it does not work with *resp (I get back 0 and I have to allocate memory).

My last unsuccessfull try via PB-way was:

Code: Select all

Procedure p_conv(num_msg.i,*msg.i,*resp.i,*appdataPtr)
  Protected *pam_msg.pam_message
  Protected *pam_resp.pam_response
  Protected *remember_first.i

  *pam_msg = PeekI(*msg.i)
  ;*pam_resp = PeekI(*resp.i)
  *pam_resp = AllocateMemory(num_msg * SizeOf(pam_response))
  If Not *pam_resp
    ProcedureReturn(19)
  Endif
  *remember_first = *pam_resp
  For i = 1 to num_msg
    Select *pam_msg\msg_style.i
      Case 1 ; Means Echo off
        *pam_resp\resp.i = *currentPW
        *pam_resp\resp_retcode = 0
      Default
        FreeMemory(*remember_first)
        ProcedureReturn(19); PAM_CONV_ERR
    EndSelecT
    *pam_resp + SizeOf(pam_response)
    *pam_msg + SizeOf(pam_message)
  Next

  *resp = *remember_first

  ProcedureReturn(0)
EndProcedure
But it still doesn't do the same as the wrapper above.


Greetings,
auser