www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Wrapper for PAM

reply Alexander Zhirov <azhirov1991 gmail.com> writes:
I want to try to make access via D to PAM. I'm trying to write 
basic things. I would like to know how to best transform access 
to callback functions? For example, so that there is no need to 
cast to a type and use binding to `extern`, move all this to a 
library?

```d
extern(C):

struct pam_message {
     int msg_style;
     const(char) *msg;
}

struct pam_response {
     char *resp;
     int	resp_retcode;
}

alias conversation = int function(int num_msg, const pam_message 
**msg, pam_response **resp, void *appdata_ptr);

struct pam_conv {
     conversation *conv;
     void *appdata_ptr;
}

struct pam_handle;
alias pam_handle_t = pam_handle;

const (char) *pam_strerror(pam_handle_t *pamh, int errnum);

int pam_start(const(char) *service_name, const(char) *user, const 
pam_conv *pam_conversation, pam_handle_t **pamh);
int pam_authenticate(pam_handle_t *pamh, int flags);
int pam_end(pam_handle_t *pamh, int pam_status);
```

Below is the code that implements basic simple authentication. Is 
it possible to move `conversation_func` into a shell so as not to 
use type casting? What is the best way to implement this?

```d
struct pam_data {
     string password;
}

extern(C) {
int conversation_func(int num_msg, const pam_message **msg, 
pam_response **resp, void *appdata_ptr) {
     pam_data *data = cast(pam_data*)appdata_ptr;

     *resp = cast(pam_response *)malloc(num_msg * 
pam_response.sizeof);
     if (*resp == null) {
         return PAM_BUF_ERR;
     }

     for (int i = 0; i < num_msg; i++) {
         switch (msg[i].msg_style) {
             case PAM_PROMPT_ECHO_ON:
             case PAM_PROMPT_ECHO_OFF:
                 (*resp)[i].resp = strdup(data.password.toStringz);
                 (*resp)[i].resp_retcode = 0;
                 break;
             default:
                 (*resp)[i].resp = null;
                 (*resp)[i].resp_retcode = 0;
                 break;
         }
     }

     return PAM_SUCCESS;
}
}

int authenticate_user(string username, string password) {
     pam_handle_t *pamh = null;
     int retval = 0;

     pam_data data = { password };
     void *appdata_ptr = &data;

     pam_conv conv = { cast(conversation*)&conversation_func, 
appdata_ptr };

     retval = pam_start("login", username.toStringz, &conv, &pamh);
     if (retval != PAM_SUCCESS) {
         writefln("pam_start: %s", pam_strerror(pamh, 
retval).to!string);
         return 1;
     }

     retval = pam_authenticate(pamh, 0);
     if (retval != PAM_SUCCESS) {
         writefln("Authentication failure: %s", pam_strerror(pamh, 
retval).to!string);
         pam_end(pamh, retval);
         return 2;
     }

     retval = pam_end(pamh, PAM_SUCCESS);
     if (retval != PAM_SUCCESS) {
         writefln("pam_end: %s", pam_strerror(pamh, 
retval).to!string);
         return 3;
     }

     return 0;
}

int main(string[] args) {
     string username = args[1];
     string password = args[2];

     int result = authenticate_user(username, password);
     if (result == 0) {
         writeln("Authentication succeeded!");
     } else {
         writefln("Authentication failed with code: %d", result);
     }

     return EXIT_SUCCESS;
}
```
Oct 03
next sibling parent reply Salih Dincer <salihdb hotmail.com> writes:
On Thursday, 3 October 2024 at 22:54:53 UTC, Alexander Zhirov 
wrote:
 I want to try to make access via D to PAM.

 ```d
 /// I added it here:
 import pam_wrapper;
 int main(string[] args)
 {
    if (args.length < 3)
     {
         writeln("Usage: ", args[0], "<username> <password>");
         return 1;
     }
 /// I added it here.
     string username = args[1];
     string password = args[2];

     int result = authenticate_user(username, password);
     if (result == 0) {
         writeln("Authentication succeeded!");
     } else {
         writefln("Authentication failed with code: %d", result);
     }

     return EXIT_SUCCESS;
 }
 ```
It is possible to implement PAM (Pluggable Authentication Modules) support in the D programming language using the standard library. The D standard library provides extern(C) support to access C language APIs and a strong FFI (Foreign Function Interface) support to adapt to C data structures. However, D itself does not include a special module for PAM, so it is necessary to work with C-based PAM libraries in the D language. I think you should use 2 separate modules! This can make type conversions and memory management safer. Here is the pam_wrapper.d file: ```d module pam_wrapper; import pam; // You have this module. import std.string, std.conv : to; import core.stdc.string : strdup; import core.stdc.stdlib : malloc, free; public import std.stdio; struct pam_data { string password; } extern(C) { int conversation_func(int num_msg, const pam_message **msg, pam_response **resp, void *appdata_ptr) { pam_data *data = cast(pam_data*)appdata_ptr; *resp = cast(pam_response *)malloc(num_msg * pam_response.sizeof); if (resp == null) return PAM_BUF_ERR; for (int i = 0; i < num_msg; i++) { switch (msg[i].msg_style) { case PAM_PROMPT_ECHO_ON: goto case; case PAM_PROMPT_ECHO_OFF: resp[i].resp = strdup(data.password.toStringz); resp[i].resp_retcode = 0; break; default: resp[i].resp = null; resp[i].resp_retcode = 0; break; } } return PAM_SUCCESS; } } int authenticate_user(string username, string password) { pam_handle_t *pamh = null; int retval = 0; pam_data data = { password }; void *appdata_ptr = &data; pam_conv conv = { cast(conversation*)&conversation_func, appdata_ptr }; retval = pam_start("login", username.toStringz, &conv, &pamh); if (retval != PAM_SUCCESS) { pam_strerror(pamh, retval).to!string.writefln!"pam_start: %s"; return 1; } retval = pam_authenticate(pamh, 0); if (retval != PAM_SUCCESS) { pam_strerror(pamh, retval).to!string.writefln!"Authentication failure: %s"; pam_end(pamh, retval); return 2; } retval = pam_end(pamh, PAM_SUCCESS); if (retval != PAM_SUCCESS) { pam_strerror(pamh, retval).to!string.writefln!"pam_end: %s"; return 3; } return 0; } ``` SDB 79
Oct 03
parent reply Alexander Zhirov <azhirov1991 gmail.com> writes:
On Thursday, 3 October 2024 at 23:56:37 UTC, Salih Dincer wrote

I meant taking the function to D for the elements of the C 
syntax. To get only an API to which we can pass our callback, and 
hide everything else inside the wrapper. I have no experience 
porting from C to D, but I would like to try to learn the basics 
to make a small library for PAM.
Oct 04
parent Salih Dincer <salihdb hotmail.com> writes:
On Friday, 4 October 2024 at 12:39:45 UTC, Alexander Zhirov wrote:
 On Thursday, 3 October 2024 at 23:56:37 UTC, Salih Dincer wrote

 I meant taking the function to D for the elements of the C 
 syntax. To get only an API to which we can pass our callback, 
 and hide everything else inside the wrapper. I have no 
 experience porting from C to D, but I would like to try to 
 learn the basics to make a small library for PAM.
https://dlang.org/spec/importc.html
Oct 04
prev sibling parent reply Alexander Zhirov <azhirov1991 gmail.com> writes:
I tried to build a class with a private function 
`conversation_func` to call it inside the public authentication 
function. When compiling I get this message:
```
source/login/auth.d(87,46): Deprecation: casting from extern (C) 
int delegate(int num_msg, const(pam_message**) msg, 
pam_response** resp, void* appdata_ptr) to extern (C) int 
function(int, const(pam_message**), pam_response**, void*)* is 
deprecated
```
And when I start up, at the moment of calling the authentication 
function, I get `Segmentation fault`.

Is the function identified differently in the class? How can this 
be fixed?
Oct 07
next sibling parent Alexander Zhirov <azhirov1991 gmail.com> writes:
On Monday, 7 October 2024 at 08:23:08 UTC, Alexander Zhirov wrote:

```
module login.auth;

import libpam;

enum {
     AUTH_SUCCESS = 0,
     AUTH_ERR_USER = 1,
     AUTH_ERR_PASS = 2,
     AUTH_ERR_NPASS = 3,
     AUTH_ERR_START = 4,
     AUTH_ERR_AUTH = 5,
     AUTH_ERR_ACCT = 6,
     AUTH_ERR_CHTOK = 7,
     AUTH_ERR_END = 8
}

class Auth {
private:
     struct PAMdata {
         string password;
         string newPassword;
     }

     extern(C) {
         int conversation_func(int num_msg, const pam_message 
**msg, pam_response **resp, void *appdata_ptr) {
             PAMdata *data = cast(PAMdata*)appdata_ptr;

             pam_response *responses = cast(pam_response 
*)calloc(num_msg, pam_response.sizeof);

             if (responses == null) {
                 return PAM_BUF_ERR;
             }

             for (int count = 0; count < num_msg; ++count) {
                 responses[count].resp_retcode = 0;
                 switch (msg[count].msg_style) {
                     case PAM_PROMPT_ECHO_ON:
                     case PAM_PROMPT_ECHO_OFF:
                         switch (msg[count].msg.to!string) {
                             case "New password: ":
                             case "Retype new password: ":
                                 responses[count].resp = 
strdup(data.newPassword.toStringz);
                                 break;
                             case "Password: ":
                             case "Current password: ":
                                 responses[count].resp = 
strdup(data.password.toStringz);
                                 break;
                             default:
                                 responses[count].resp = null;
                                 break;
                         }
                         break;
                     default:
                         responses[count].resp = null;
                         break;
                 }
             }

             *resp = responses;

             return PAM_SUCCESS;
         }
     }
public:
     int authenticate(string username, string password) {
         if (!username.length) {
             return AUTH_ERR_USER;
         }

         if (!password.length) {
             return AUTH_ERR_PASS;
         }

         pam_handle_t *pamh = null;
         int retval = 0;

         PAMdata data = { password };
         void *appdata_ptr = &data;

         pam_conv conv = { 
cast(conversation*)&this.conversation_func, appdata_ptr };

         retval = pam_start("login", username.toStringz, &conv, 
&pamh);
         if (retval != PAM_SUCCESS) {
             return AUTH_ERR_START;
         }

         retval = pam_authenticate(pamh, 0);
         if (retval != PAM_SUCCESS) {
             pam_end(pamh, retval);
             return AUTH_ERR_AUTH;
         }

         retval = pam_end(pamh, PAM_SUCCESS);
         if (retval != PAM_SUCCESS) {
             return AUTH_ERR_END;
         }

         return AUTH_SUCCESS;
     }
}

```
Oct 07
prev sibling next sibling parent evilrat <evilrat666 gmail.com> writes:
On Monday, 7 October 2024 at 08:23:08 UTC, Alexander Zhirov wrote:
 I tried to build a class with a private function 
 `conversation_func` to call it inside the public authentication 
 function. When compiling I get this message:
 ```
 source/login/auth.d(87,46): Deprecation: casting from extern 
 (C) int delegate(int num_msg, const(pam_message**) msg, 
 pam_response** resp, void* appdata_ptr) to extern (C) int 
 function(int, const(pam_message**), pam_response**, void*)* is 
 deprecated
 ```
 And when I start up, at the moment of calling the 
 authentication function, I get `Segmentation fault`.

 Is the function identified differently in the class? How can 
 this be fixed?
Of course it segfaults, you are trying to give delegate pretending it is to be a standalone function. For example you can fix it by using a trampoline - Make a global class instance somewhere, make conversation_func static (if you absolutely want it to be in class) and then in that function use instance.actual_conversation_func, that will do actual work. Maybe other solutions exists that is not variation of this method by I can't come up with anything else.
Oct 07
prev sibling parent reply Salih Dincer <salihdb hotmail.com> writes:
On Monday, 7 October 2024 at 08:23:08 UTC, Alexander Zhirov wrote:
 I tried to build a class with a private function 
 `conversation_func` to call it inside the public authentication 
 function. When compiling I get this message:
 ```
 source/login/auth.d(87,46): Deprecation: casting from extern 
 (C) int delegate(int num_msg, const(pam_message**) msg, 
 pam_response** resp, void* appdata_ptr) to extern (C) int 
 function(int, const(pam_message**), pam_response**, void*)* is 
 deprecated
 ```
 And when I start up, at the moment of calling the 
 authentication function, I get `Segmentation fault`.

 Is the function identified differently in the class? How can 
 this be fixed?
Would you try static this? ```d // 1. Salih changed it: extern(C) static int conversation_func(int num_msg, const pam_message **msg, pam_response **resp, void *appdata_ptr) { /* ... */} ``` There is no need for `this`. ```d // 2. Salih changed it: pam_conv conv = { cast(conversation*)&/*this.*/conversation_func, appdata_ptr }; ``` SDB 79
Oct 10
parent reply Salih Dincer <salihdb hotmail.com> writes:
On Thursday, 10 October 2024 at 12:58:22 UTC, Salih Dincer wrote:
```d
class Auth
{
   private:
     struct PAMdata
     {
       string password;
       string newPassword;
     }

     extern(C)
     { // 1. Salih changed it:
       static int conversation_func(int num_msg,
                        const pam_message **msg,
                            pam_response **resp,
                              void *appdata_ptr)
       {
         auto data = cast(PAMdata*)appdata_ptr;
         auto responses = cast(pam_response*)
           calloc(num_msg, pam_response.sizeof);

         if (responses == null)
         {
           return PAM_BUF_ERR;
         }

         for (int i = 0; i < num_msg; ++i)
         {
           responses[i].resp_retcode = 0;
           switch (msg[i].msg_style)
           {
             case PAM_PROMPT_ECHO_ON: goto case;
             case PAM_PROMPT_ECHO_OFF:
               switch (msg[i].msg.to!string)
               {
                 case "New password: ": goto case;
                 case "Retype new password: ":
                   responses[i].resp = 
strdup(data.newPassword.toStringz);
                   break;
                 case "Password: ": goto case;
                 case "Current password: ":
                   responses[i].resp = 
strdup(data.password.toStringz);
                   break;
                 default:
                   responses[i].resp = null;
                   break;
               }
               break;
             default:
               responses[i].resp = null;
               break;
           }
         }
         *resp = responses;
         return PAM_SUCCESS;
       }
     }

   public:
     int authenticate(string username, string password)
     {
       if (!username.length)
       {
         return AUTH_ERR_USER;
       }

       if (!password.length)
       {
         return AUTH_ERR_PASS;
       }

       pam_handle_t *pamh = null;
       PAMdata data = {
         password
       };

       void *appdata_ptr = &data;
       // 2. Salih changed it:
       pam_conv conv = {
         cast(conversation*)&conversation_func,
         appdata_ptr
       };
       // 3. Salih changed it:
       auto retval = pam_start("login", username.toStringz, &conv, 
&pamh);
       if (retval != PAM_SUCCESS)
       {
         return AUTH_ERR_START;
       }

       retval = pam_authenticate(pamh, 0);
       if (retval != PAM_SUCCESS)
       {
         pam_end(pamh, retval);
         return AUTH_ERR_AUTH;
       }

       retval = pam_end(pamh, PAM_SUCCESS);
       if (retval != PAM_SUCCESS)
       {
         return AUTH_ERR_END;
       }
       return AUTH_SUCCESS;
     }
}
```

SDB 79
Oct 10
parent Alexander Zhirov <azhirov1991 gmail.com> writes:
On Thursday, 10 October 2024 at 13:10:58 UTC, Salih Dincer wrote:
 On Thursday, 10 October 2024 at 12:58:22 UTC, Salih Dincer 
 wrote:
 ```d
 class Auth
 {
   private:
     struct PAMdata
     {
       string password;
       string newPassword;
     }

     extern(C)
     { // 1. Salih changed it:
       static int conversation_func(int num_msg,
                        const pam_message **msg,
                            pam_response **resp,
                              void *appdata_ptr)
       {
         auto data = cast(PAMdata*)appdata_ptr;
         auto responses = cast(pam_response*)
           calloc(num_msg, pam_response.sizeof);

         if (responses == null)
         {
           return PAM_BUF_ERR;
         }

         for (int i = 0; i < num_msg; ++i)
         {
           responses[i].resp_retcode = 0;
           switch (msg[i].msg_style)
           {
             case PAM_PROMPT_ECHO_ON: goto case;
             case PAM_PROMPT_ECHO_OFF:
               switch (msg[i].msg.to!string)
               {
                 case "New password: ": goto case;
                 case "Retype new password: ":
                   responses[i].resp = 
 strdup(data.newPassword.toStringz);
                   break;
                 case "Password: ": goto case;
                 case "Current password: ":
                   responses[i].resp = 
 strdup(data.password.toStringz);
                   break;
                 default:
                   responses[i].resp = null;
                   break;
               }
               break;
             default:
               responses[i].resp = null;
               break;
           }
         }
         *resp = responses;
         return PAM_SUCCESS;
       }
     }

   public:
     int authenticate(string username, string password)
     {
       if (!username.length)
       {
         return AUTH_ERR_USER;
       }

       if (!password.length)
       {
         return AUTH_ERR_PASS;
       }

       pam_handle_t *pamh = null;
       PAMdata data = {
         password
       };

       void *appdata_ptr = &data;
       // 2. Salih changed it:
       pam_conv conv = {
         cast(conversation*)&conversation_func,
         appdata_ptr
       };
       // 3. Salih changed it:
       auto retval = pam_start("login", username.toStringz, 
 &conv, &pamh);
       if (retval != PAM_SUCCESS)
       {
         return AUTH_ERR_START;
       }

       retval = pam_authenticate(pamh, 0);
       if (retval != PAM_SUCCESS)
       {
         pam_end(pamh, retval);
         return AUTH_ERR_AUTH;
       }

       retval = pam_end(pamh, PAM_SUCCESS);
       if (retval != PAM_SUCCESS)
       {
         return AUTH_ERR_END;
       }
       return AUTH_SUCCESS;
     }
 }
 ```

 SDB 79
Thanks for the example. I'll try to test it.
Oct 11