digitalmars.D.learn - Wrapper for PAM
- Alexander Zhirov (101/101) Oct 03 I want to try to make access via D to PAM. I'm trying to write
- Salih Dincer (83/106) Oct 03 It is possible to implement PAM (Pluggable Authentication
- Alexander Zhirov (6/6) Oct 04 On Thursday, 3 October 2024 at 23:56:37 UTC, Salih Dincer wrote
- Salih Dincer (2/8) Oct 04 https://dlang.org/spec/importc.html
- Alexander Zhirov (14/14) Oct 07 I tried to build a class with a private function
- Alexander Zhirov (92/92) Oct 07 On Monday, 7 October 2024 at 08:23:08 UTC, Alexander Zhirov wrote:
- evilrat (9/23) Oct 07 Of course it segfaults, you are trying to give delegate
- Salih Dincer (17/31) Oct 10 Would you try static this?
- Salih Dincer (101/101) Oct 10 On Thursday, 10 October 2024 at 12:58:22 UTC, Salih Dincer wrote:
- Alexander Zhirov (2/104) Oct 11 Thanks for the example. I'll try to test it.
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
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
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
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
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
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
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
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
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
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 79Thanks for the example. I'll try to test it.
Oct 11