1 //===========================================
2 //  Lumina-DE source code
3 //  Copyright (c) 2015, Ken Moore
4 //  Available under the 3-clause BSD license
5 //  See the LICENSE file for full details
6 //===========================================
7 // This function provides the basic current-user password validation
8 // The binary may need to have an effective root UID (setuid as root: "chmod 4555")
9 //   so that PAM can actually check the validity of the password.
10 //===========================================
11 //  SECURITY NOTE:
12 //    It is highly recomended that you have your PAM rules setup to disallow password checks for a time
13 //    after a number of failed attempts to prevent a user-level script from hammering this utility
14 //===========================================
15 //Standard C libary
16 #include <unistd.h>	// Standard C
17 #include <stdlib.h>
18 #include <stdio.h> 	// Usage output
19 #include <pwd.h>  		// User DB information
20 #include <string.h>
21 
22 //PAM/security libraries
23 #include <sys/types.h>
24 #include <security/pam_appl.h>
25 #include <security/openpam.h>
26 
showUsage()27 void showUsage(){
28     puts("lumina-checkpass: Simple user-level check for password validity (for screen unlockers and such).");
29     puts("Usage:");
30     //puts("  lumina-checkpass <password>");
31     puts("  lumina-checkpass -fd <file descriptor>");
32     puts("  lumina-checkpass -f <file path>");
33     puts("Returns: 0 for a valid password, 1 for invalid");
34 }
35 
main(int argc,char ** argv)36 int main(int argc, char** argv){
37   //Check the inputs
38   if(argc!=3){
39     //Invalid inputs - show the help text
40     showUsage();
41     return 1;
42   }
43   char*pass = 0;
44   if(argc==3 && 0==strcmp(argv[1],"-fd") ){
45     FILE *fp = fdopen(atoi(argv[2]), "r");
46     size_t len;
47     if(fp!=0){
48       ssize_t slen = getline(&pass, &len, fp);
49       if(pass[slen-1]=='\n'){ pass[slen-1] = '\0'; }
50     }
51     fclose(fp);
52   }else if(argc==3 && 0==strcmp(argv[1],"-f") ){
53     FILE *fp = fopen(argv[2], "r");
54     size_t len;
55     if(fp!=0){
56       ssize_t slen = getline(&pass, &len, fp);
57       if(pass[slen-1]=='\n'){ pass[slen-1] = '\0'; }
58     }else{
59       puts("[ERROR] Unknown option provided");
60       puts("----------------");
61       showUsage();
62       return 1;
63     }
64     fclose(fp);
65   }
66   if(pass == 0){ puts("Could not read password!!"); return 1; } //error in reading password
67   //puts("Read Password:");
68   //puts(pass);
69   //Validate current user (make sure current UID matches the logged-in user,
70   char* cUser = getlogin();
71   struct passwd *pwd = 0;
72   pwd = getpwnam(cUser);
73   if(pwd==0){ return 1; } //Login user could not be found in the database? (should never happen)
74   if( getuid() != pwd->pw_uid ){ return 1; } //Current UID does not match currently logged-in user UID
75   //Create the non-interactive PAM structures
76   pam_handle_t *pamh;
77   struct pam_conv pamc = { openpam_nullconv, NULL };
78     //Place the user-supplied password into the structure
79     int ret = pam_start( "system", cUser, &pamc, &pamh);
80     if(ret != PAM_SUCCESS){ return 1; } //could not init PAM
81     //char* cPassword = argv[1];
82     ret = pam_set_item(pamh, PAM_AUTHTOK, pass);
83     //Authenticate with PAM
84     ret = pam_authenticate(pamh,0); //this can be true without verifying password if pam_self.so is used in the auth procedures (common)
85     if( ret == PAM_SUCCESS ){ ret = pam_acct_mgmt(pamh,0); } //Check for valid, unexpired account and verify access restrictions
86     //Stop the PAM instance
87     pam_end(pamh,ret);
88   //return verification result
89   return ((ret==PAM_SUCCESS) ? 0 : 1);
90 }
91