How to Authenticate Username/Password Using Pam W/O Root Privileges

How to authenticate username/password using PAM w/o root privileges

The application needed to be able to read /etc/shadow.

See my post for one way to do this.

EDIT:
Add post from above link in case the link ever breaks

I wrote authentication module in C++ which alows to check
username/password through PAM in Linux (I’m using Fedora Linux). I
would like to share with you, what I made :-) . So, let’s go :-)

Prerequisities:

Install package pam-devel
(This step is necessary when you use shadow password) Create new Linux user and group. Set this group as default for this user. Then

follow with these steps:
Go to /etc
Log in as root (su)
Change group to the new one for file shadow (chgrp new_group shadow)
Set ‘read’ privilage for this group (chmod 0440 shadow)

Write this code: (authModule.c) view plaincopy to clipboardprint?

#include <stdio.h>  
#include <security/pam_appl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

struct pam_response *reply;

// //function used to get user input
int function_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
{
*resp = reply;
return PAM_SUCCESS;
}

int authenticate_system(const char *username, const char *password)
{
const struct pam_conv local_conversation = { function_conversation, NULL };
pam_handle_t *local_auth_handle = NULL; // this gets set by pam_start

int retval;
retval = pam_start("su", username, &local_conversation, &local_auth_handle);

if (retval != PAM_SUCCESS)
{
printf("pam_start returned: %d\n ", retval);
return 0;
}

reply = (struct pam_response *)malloc(sizeof(struct pam_response));

reply[0].resp = strdup(password);
reply[0].resp_retcode = 0;
retval = pam_authenticate(local_auth_handle, 0);

if (retval != PAM_SUCCESS)
{
if (retval == PAM_AUTH_ERR)
{
printf("Authentication failure.\n");
}
else
{
printf("pam_authenticate returned %d\n", retval);
}
return 0;
}

printf("Authenticated.\n");
retval = pam_end(local_auth_handle, retval);

if (retval != PAM_SUCCESS)
{
printf("pam_end returned\n");
return 0;
}

return 1;
}

int main(int argc, char** argv)
{
char* login;
char* password;

printf("Authentication module\n");

if (argc != 3)
{
printf("Invalid count of arguments %d.\n", argc);
printf("./authModule <username> <password>");
return 1;
}

login = argv[1];
password = argv[2];

if (authenticate_system(login, password) == 1)
{
printf("Authenticate with %s - %s through system\n", login, password);
return 0;
}

printf("Authentication failed!\n");
return 1;
}

Compile code:

gcc -o authModule authModule.c -lpam  

Run code (as the new user!):

./authModule user password  

That’s all!! :-) Hope it helps!

Configuring MongoDB to authenticate user's password via Linux PAM

Unfortunately, MongoDB authentication using PAM Linux seems to be configurable only in MongoDB Enterprise Edition.

This is because PAM Authentication requires PLAIN Authentication Mechanism, available only in MongoDB Enterprise Edition as mentionned in the documentation:

PLAIN (LDAP SASL) External authentication using LDAP. You can also use PLAIN for authenticating in-database users. PLAIN transmits passwords in plain text. This mechanism is available only in MongoDB Enterprise.

BTW, in MongoDB Enterprise Edition, you can enable PAM Authentication using the following (tested on Debian Stretch):

Install saslauthd

apt-get install sasl2-bin

vi /etc/default/saslauthd

START=yes

/etc/init.d/saslauthd restart

At this step you may test your sasl configuration with ("myuser" is your unix user):

testsaslauthd -u <myuser> -p <SecretPassword>

This should output a success message:

0: OK "Success."

Create a MongoDB user "myuser"

Replace "myuser" with the user with whom you want to authenticate.

mongo admin

db.getSiblingDB("$external").createUser(
{
user : "myuser",
roles: [ { role: "read", db: "mydb" } ]
}
)

Configure MongoDB to enable PLAIN Authentication Mechanism

vi /etc/mongod.conf

security:
authorization: enabled

setParameter:
authenticationMechanisms: PLAIN,MONGODB-X509,SCRAM-SHA-1,SCRAM-SHA-256

You should add the (Linux) mongodb user to the sasl group (this makes sure that MongoDB has the permission to access saslauthd)

adduser mongodb sasl

Restart mongod

systemctl restart mongod.service

Connect to MongoDB

Now, on MongoDB Enterprise, you should be able to authenticate using your linux username/pwd:

mongo --authenticationMechanism=PLAIN --authenticationDatabase='$external' -u myuser mydb

MongoDB shell version v4.0.7
connecting to: mongodb://127.0.0.1:27017/mydb?authMechanism=PLAIN&authSource=%24external&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("********-****-****-****-************") }
MongoDB server version: 4.0.7
MongoDB Enterprise >

On MongoDB Community Edition, it sadly fails with an "Unsupported mechanism" error:

MongoDB shell version v4.0.7
connecting to: mongodb://127.0.0.1:27017/mydb?authMechanism=PLAIN&authSource=%24external&gssapiServiceName=mongodb
2019-03-25T18:26:51.307+0100 E QUERY [js] Error: Unsupported mechanism 'PLAIN' on authentication database '$external' :
connect@src/mongo/shell/mongo.js:343:13
@(connect):3:6
exception: connect failed

How to set up diferent password rules for regular users and for root on PAM

Note that root is not asked for an old password so the checks that compare the old and new password are not performed.
So, basically, the phrase

The following rule does not apply to the root password

means you can't make difok=7 work for root and not that you must create a separate rule for root.

PAM Authentication for a Legacy Application

This is what I ended up doing. See the comment marked with three asterisks.

#include <stdlib.h>
#include <iostream>
#include <fstream>
#include <security/pam_appl.h>
#include <unistd.h>

// To build this:
// g++ test.cpp -lpam -o test

// if pam header files missing try:
// sudo apt install libpam0g-dev

struct pam_response *reply;

//function used to get user input
int function_conversation(int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr)
{
*resp = reply;
return PAM_SUCCESS;
}

int main(int argc, char** argv)
{
if(argc != 2) {
fprintf(stderr, "Usage: check_user <username>\n");
exit(1);
}
const char *username;
username = argv[1];

const struct pam_conv local_conversation = { function_conversation, NULL };
pam_handle_t *local_auth_handle = NULL; // this gets set by pam_start

int retval;

// local_auth_handle gets set based on the service
retval = pam_start("common-auth", username, &local_conversation, &local_auth_handle);

if (retval != PAM_SUCCESS)
{
std::cout << "pam_start returned " << retval << std::endl;
exit(retval);
}

reply = (struct pam_response *)malloc(sizeof(struct pam_response));

// *** Get the password by any method, or maybe it was passed into this function.
reply[0].resp = getpass("Password: ");
reply[0].resp_retcode = 0;

retval = pam_authenticate(local_auth_handle, 0);

if (retval != PAM_SUCCESS)
{
if (retval == PAM_AUTH_ERR)
{
std::cout << "Authentication failure." << std::endl;
}
else
{
std::cout << "pam_authenticate returned " << retval << std::endl;
}
exit(retval);
}

std::cout << "Authenticated." << std::endl;

retval = pam_end(local_auth_handle, retval);

if (retval != PAM_SUCCESS)
{
std::cout << "pam_end returned " << retval << std::endl;
exit(retval);
}

return retval;
}


Related Topics



Leave a reply



Submit