Niedermayer.ca
Published on Niedermayer.ca (https://niedermayer.ca)

Home > Code > Pluggable Authentication Modules (PAM)

Pluggable Authentication Module (PAM)

The Great Plains Free-Net was looking to create a User Management System for the users of its Internet service accounts. Their environment heavily leveraged RedHat  Linux 6.x at the time and there was a significant need to allow users to change their passwords on the system using a web-based interface. Unfortunately at the time, there was no library for PERL or any other web-friendly language.

I wrote this Application Package Interface (API) to interact with the Linux' "Pluggable Authentication Module" or PAM routines. Although the code is in C, it can easily be called as a system call from any scripting or programming language.

The files contain:

  1. The source code
  2. The makefile to create the entire package (if you are unfamiliar with makefiles, they are easy to learn)
  3. The list of required library files on the system to support this API
  4. A typescript output showing a test run of the compiled code.
  • Log in [1] to post comments
Language: 
C
Archive: 
Package icon cs_password.zip [2]

Project makefile

Project Name: 
Pluggable Authentication Module (PAM) [3]
  • Log in [4] to post comments
Filename: 
makefile.txt
Language: 
makefile
Summary: 

This makefile will compile the code with the  necessary compiler flags for a Linux  system.

Code: 
cs_password: ./cs_password.c gcc -o cs_password ./cs_password.c -lpam -lpam_misc -lcrack

Changing Passwords Source File

Project Name: 
Pluggable Authentication Module (PAM) [3]
  • Log in [5] to post comments
Filename: 
cs_password.c
Language: 
C
Code: 
/************************************************************ ** Library functions to interact with the Linux-PAM ** ** modules in order to update a user's password on ** ** the system. ** ** ** ** Make sure you add the following lines to the ** ** pam.conf file (ore equivalent): ** ** cs_password auth required ** ** /lib/security/pam_unix_auth.so ** ** cs_password account required ** ** /lib/security/pam_unix_acct.so ** ** cs_password password required ** ** /lib/security/pam_unix_passwd.so ** ** cs_password session required ** ** /lib/security/pam_unix_acct.so ** ** ** ** Author: Daryle Niedermayer (dpn) ** ** daryle@niedermayer.ca ** ** Date: 2002-06-17 ** ** ** ** $Id: cs_password.c,v 1.2 2002/06/20 19:51:24 root Exp root $ ** $Log: cs_password.c,v $ ** Revision 1.2 2002/06/20 19:51:24 root ** Fully documented and debugged test of how to change a password. ** ** Revision 1.1 2002/06/19 16:26:19 root ** Initial revision **: ************************************************************/ #include <stdio.h> #include <stdlib.h> #include <security/pam_appl.h> #include <security/pam_misc.h> #include <crack.h> #define CS_WEAK_PASSWORD -3 #define CS_BAD_DATA -2 #define CS_BAD_USAGE -1 #define CS_SUCCESS 0 #define COPY_STRING(s) (s) ? strdup(s) : NULL /* DEFINE STATIC EXTERNAL STRUCTURES AND VARIABLES SO THAT THEY ONLY HAVE SCOPE WITHIN THE METHODS AND FUNCTIONS OF THIS SOURCE FILE */ static char* service_name = "cs_password"; static char* user; static char* old_password; static char* new_password; static int PAM_conv (int, const struct pam_message**, struct pam_response**, void*); static struct pam_conv PAM_converse = {PAM_conv, NULL}; /************************************************* ** PAM Conversation function ** *************************************************/ static int PAM_conv (int num_msg, const struct pam_message **msg, struct pam_response **resp, void *appdata_ptr) { int replies = 0; struct pam_response *reply = NULL; reply = malloc(sizeof(struct pam_response) * num_msg); if (!reply) return PAM_CONV_ERR; for (replies = 0; replies < num_msg; replies++) { printf("***Message from PAM is: |%s|\n", msg[replies]->msg); printf("***Msg_style to PAM is: |%d|\n", msg[replies]->msg_style); if (! strcmp(msg[replies]->msg,"Password: ")) { printf("***Sending old password\n"); reply[replies].resp = COPY_STRING(old_password); } if (! strcmp(msg[replies]->msg,"Enter new UNIX password: ")) { printf("***Sending new password\n"); reply[replies].resp = COPY_STRING(new_password); } if (! strcmp(msg[replies]->msg,"Retype new UNIX password: ")) { printf("***Sending new password again\n"); reply[replies].resp = COPY_STRING(new_password); } printf("***Response to PAM is: |%s|\n", reply[replies].resp); } *resp = reply; return PAM_SUCCESS; } /************************************************* ** MAIN PROCEDURE ** *************************************************/ int main(int argc, char *argv[]) { /* DEFINITIONS */ pam_handle_t* pamh = NULL; int retval; char* pw_check; char* dict_path = "/usr/lib/cracklib_dict"; /* DETERMINE IF VARIABLE COUNT IS CORRECT */ if (argc != 4) { printf("Usage: pam_passwd <USER> <OLD_PASSWORD> <NEW_PASSWORD>\n"); exit (CS_BAD_USAGE); } /* PARSE PARAMETERS FROM INPUTS */ user = argv[1]; old_password = argv[2]; new_password = argv[3]; if (!(user && old_password && strlen(user) && strlen(old_password))) { printf("Incorrect/invalid username or old password"); exit (CS_BAD_DATA); } /* SEE IF THE NEW PASSWORD IS SUITABLY TOUGH */ /* This should be preprocessed outside of this function. */ printf("Testing for weak passwords...\n"); pw_check = FascistCheck(new_password, dict_path); if (pw_check != NULL) { printf("***Weak Password!: |%s|\n",pw_check); exit(CS_WEAK_PASSWORD); } /* GET A HANDLE TO A PAM INSTANCE */ printf("Trying to get a handle to the PAM service...\n"); retval = pam_start(service_name, user, &PAM_converse, &pamh); /* IS THE USER REALLY A USER? */ if (retval == PAM_SUCCESS) { printf("...Service handle was created.\n"); printf("Trying to see if the user is a valid system user...\n"); retval = pam_authenticate(pamh, 0); } else { printf("...Call to create service handle failed with error: %d\n",retval); } /* IS USER PERMITTED ACCESS? */ if (retval == PAM_SUCCESS) { printf("...User %s is a real user.\n",user); printf("Trying to pass info to the pam_acct_mgmt function...\n"); retval = pam_acct_mgmt(pamh, 0); } else { if (retval == PAM_USER_UNKNOWN) printf("...Failed to find user %s with error: %d\n",user,retval); else printf("...Failed to authenticate for an unknown error: %d\n",retval); } if (retval == PAM_SUCCESS) { printf("...User %s is permitted access.\n",user); } else { printf("...cs_password error: User %s is not authenticated\n",user); printf("...Call returned with error: %d\n",retval); } /* TRYING TO CHANGE THE PASSWORD AUTHENTICATION TOKEN */ if (retval == PAM_SUCCESS) { printf("Trying to update the password token...\n"); retval = pam_chauthtok(pamh, PAM_SILENT); } if (retval == PAM_SUCCESS) { printf("...Successfully updated password!\n"); } else { printf("...Failed to update password: error %d\n",retval); printf("...%s\n",pam_strerror(pamh, retval)); } /* CLEAN UP OUR HANDLES AND VARIABLES */ if (pam_end(pamh, retval) != PAM_SUCCESS) { pamh = NULL; fprintf(stderr, "cs_password error: Failed to release authenticator\n"); } exit (CS_SUCCESS); }

PAM Configuration Directives

Project Name: 
Pluggable Authentication Module (PAM) [3]
  • Log in [6] to post comments
Filename: 
cs_password.txt
Language: 
System Configuration
Summary: 

These directives need to be added  to the /etc/pam.d/ or /etc/init.d/ folder on the system.

Code: 
auth required /lib/security/pam_unix_auth.so account required /lib/security/pam_unix_acct.so password required /lib/security/pam_unix_passwd.so

Sample output from a test run

Project Name: 
Pluggable Authentication Module (PAM) [3]
  • Log in [7] to post comments
Filename: 
typescript.txt
Language: 
Text file
Summary: 

This file shows a sample run with the application showing the traffic and messages back and forth.

Code: 
[root@daryle lib]# ./cs_password guest 1guest Usage: pam_passwd <USER> <OLD_PASSWORD> <NEW_PASSWORD> [root@daryle lib]# ./cs_password guest guest guest1 Testing for weak passwords... ***Weak Password!: |it is based on a dictionary word| [root@daryle lib]# ./cs_password guest guest Guest1 Testing for weak passwords... Trying to get a handle to the PAM service... ...Service handle was created. Trying to see if the user is a valid system user... ***Message from PAM is: |Password: | ***Msg_style to PAM is: |1| ***Sending old password ***Response to PAM is: |oldguest| ...User guest is a real user. Trying to pass info to the pam_acct_mgmt function... ...User guest is permitted access. Trying to update the password token... ***Message from PAM is: |Enter new UNIX password: | ***Msg_style to PAM is: |1| ***Sending new password ***Response to PAM is: |1Guest1| ***Message from PAM is: |Retype new UNIX password: | ***Msg_style to PAM is: |1| ***Sending new password again ***Response to PAM is: |1Guest1| ...Successfully updated password! [root@daryle lib]#

Source URL:https://niedermayer.ca/code/pam

Links
[1] https://niedermayer.ca/user/login?destination=node/233%23comment-form [2] https://niedermayer.ca/sites/default/files/cs_password.zip [3] http://www.niedermayer.ca/node/233 [4] https://niedermayer.ca/user/login?destination=node/242%23comment-form [5] https://niedermayer.ca/user/login?destination=node/243%23comment-form [6] https://niedermayer.ca/user/login?destination=node/244%23comment-form [7] https://niedermayer.ca/user/login?destination=node/245%23comment-form