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









Salih Dincer <salihdb hotmail.com> 