summaryrefslogtreecommitdiff
path: root/pam
diff options
context:
space:
mode:
authorSam Chudnick <sam@chudnick.com>2022-06-27 20:41:01 -0400
committerSam Chudnick <sam@chudnick.com>2022-06-27 20:41:01 -0400
commit570d0da295f3e2fcd7b8c80ae2e6c42fc365abdd (patch)
treeccfe8614fc644b08db5d15091c344c77fff44fb1 /pam
Initial commit
Diffstat (limited to 'pam')
-rwxr-xr-xpam/pam.py100
-rw-r--r--pam/pam_mfa.c60
2 files changed, 160 insertions, 0 deletions
diff --git a/pam/pam.py b/pam/pam.py
new file mode 100755
index 0000000..28450ee
--- /dev/null
+++ b/pam/pam.py
@@ -0,0 +1,100 @@
1#!/usr/bin/python3
2import socket
3import argparse
4import time
5import sys
6
7# Sends authentication request to MFA server
8# Receive either pass or fail response from MFA server
9# Returns status to PAM
10
11HEADER_LENGTH = 64
12KEY_LENGTH = 64
13ACK_LENGTH = 3
14DISCONNECT_MESSAGE = "DISCONNECT"
15FORMAT = "utf-8"
16RESPONSE_LENGTH = 1
17PAM_SUCCESS = 0
18PAM_AUTH_ERR = 7
19
20def parse_arguments():
21 # Parse command line arguments
22 parser = argparse.ArgumentParser()
23 parser.add_argument("--user",type=str,help="PAM username",required=True)
24 parser.add_argument("--service",type=str,help="PAM service",required=True)
25 return parser.parse_args()
26
27def init_connection(mfa_server, pam_port):
28 # Attempts to connect to MFA server with provided address and port
29 # Repeats connection attempts once per second until timeout is reached
30 # Returns the socket if connection was successful or None otherwise
31 connection = None
32 timeout = 0
33 timeout_length = 5
34 sleep_length = 1
35 while connection == None and timeout < timeout_length:
36 try:
37 connection = socket.create_connection((mfa_server,pam_port))
38 print("connected to mfa server")
39 return connection
40 except (ConnectionError,ConnectionRefusedError):
41 time.sleep(sleep_length)
42 timeout += sleep_length
43 return None
44
45
46def read_config(config_file="/etc/mfa/mfa.conf"):
47 # Read config file for server and port info
48 # Return tuple (server,port)
49 server = ""
50 port = 0
51 with open(config_file) as conf:
52 line = None
53 while line != "":
54 line = conf.readline()
55 if line.startswith("server ="):
56 server = line.split("=")[1].strip()
57 if line.startswith("port ="):
58 port = int(line.split("=")[1].strip())
59 return (server,port)
60
61def main():
62 authed = "0"
63 failed = "1"
64
65 # Get arguments
66 args = parse_arguments()
67 user = args.user
68 service = args.service
69
70 # Compile data to send to server
71 mfa_server, pam_port = read_config()
72 hostname = None
73 with open("/etc/hostname") as f:
74 hostname = f.read().strip()
75 data = user + "," + hostname + "," + service
76
77
78 # Initalize connection to MFA server. Quit if unable to connect.
79 connection = init_connection(mfa_server,pam_port)
80 if connection == None:
81 print(failed)
82 sys.exit(1)
83
84 # Send authentication data to MFA server
85 data_length = len(data)
86 length_msg = str(data_length)
87 length_msg += ' ' * (HEADER_LENGTH - len(length_msg))
88 connection.send(length_msg.encode(FORMAT))
89 connection.send(data.encode(FORMAT))
90
91 # Listen for response from MFA server
92 # Response will be 0 for authenticated and 1 for denied
93 response = int(connection.recv(RESPONSE_LENGTH).decode(FORMAT))
94
95 # Print success/failure for PAM module
96 print(str(response))
97
98if __name__ == '__main__':
99 main()
100
diff --git a/pam/pam_mfa.c b/pam/pam_mfa.c
new file mode 100644
index 0000000..7e71856
--- /dev/null
+++ b/pam/pam_mfa.c
@@ -0,0 +1,60 @@
1#include <stdlib.h>
2#include <unistd.h>
3#include <errno.h>
4#include <pwd.h>
5#include <string.h>
6#include <stdio.h>
7#include <stdbool.h>
8#include <syslog.h>
9
10#include <security/pam_modutil.h>
11#include <security/pam_modules.h>
12#include <security/pam_ext.h>
13
14#define PAMPY "python3 /usr/bin/openmfa/pam/pam.py"
15
16int pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char** argv) {
17 int retval;
18 const char *user = NULL;
19 const char *service;
20 FILE *fp;
21
22 // Get user and service
23 pam_get_item(pamh, PAM_SERVICE, (const void **) &service);
24 pam_get_user(pamh, &user, NULL);
25
26 // Build command line
27 int cmdsize = 256;
28 char cmd[cmdsize];
29 cmd[0] = '\0';
30 strcat(cmd, PAMPY);
31 strcat(cmd," --user ");
32 strcat(cmd,user);
33 strcat(cmd," --service ");
34 strcat(cmd,service);
35 pam_syslog(pamh,LOG_INFO,cmd);
36
37 // Execute pam.py
38 if ((fp = popen(cmd,"r")) == NULL) {
39 pam_syslog(pamh,LOG_ERR,"Error opening pipe");
40 return PAM_AUTH_ERR;
41 }
42
43 // Get output and return authentication status
44 int size = 32;
45 char result[size];
46 fgets(result,size,fp);
47 pam_syslog(pamh,LOG_INFO,result);
48 pclose(fp);
49 if (atoi(result) == 0) {
50 pam_syslog(pamh,LOG_INFO,"auth success");
51 return PAM_SUCCESS;
52 } else {
53 pam_syslog(pamh,LOG_ERR,"auth error");
54 return PAM_AUTH_ERR;
55 }
56}
57
58int pam_sm_setcred(pam_handle_t *pamh, int flags, int argc, const char** argv) {
59 return PAM_IGNORE;
60}