-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathpamAuth.py
More file actions
executable file
·115 lines (102 loc) · 4.11 KB
/
pamAuth.py
File metadata and controls
executable file
·115 lines (102 loc) · 4.11 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
#!/usr/bin/env python2
#File: pamAuth.py
#Project: CSIS 440 Final Project - SSH Server
#Author: Dustin Ernst (Sam Sussman, Charles Belanger)
#Provides a function to authenticate a user against the system's pam setup.
import sys
import ctypes
import ctypes.util
#These are the only two c libs we'll need, libc and libpam.
libc = ctypes.cdll.LoadLibrary(ctypes.util.find_library("c"))
libpam = ctypes.cdll.LoadLibrary(ctypes.util.find_library("pam"))
#Need to create a bunch of structure classes first
class pam_message(ctypes.Structure):
"""
http://linux.die.net/man/3/pam_conv
"""
_fields_ = [("msg_style", ctypes.c_int),("msg", ctypes.c_char_p)]
class pam_response(ctypes.Structure):
"""
http://linux.die.net/man/3/pam_conv
"""
_fields_ = [("resp", ctypes.c_char_p),("resp_retcode", ctypes.c_int)]
#Signature for our pam_conv callback function.
pam_conv_sig = ctypes.CFUNCTYPE(ctypes.c_int,\
ctypes.c_int,\
ctypes.POINTER(ctypes.POINTER(pam_message)),\
ctypes.POINTER(ctypes.POINTER(pam_response)), \
ctypes.c_void_p)
class pam_conv(ctypes.Structure):
"""
http://linux.die.net/man/3/pam_conv
"""
_fields_ = [("conv", pam_conv_sig),
("appdata_ptr",ctypes.c_void_p)]
class pam_handle_t(ctypes.Structure):
"""
http://linux.die.net/man/3/pam_start
Is apparently a blind structure.
We'll give it a void pointer for a field.
This pointer will be filled in by pam_start.
"""
_fields_ = [("pam_handle", ctypes.c_void_p)]
def __init__(self):
self.pam_handle = 0
#http://linux.die.net/man/3/pam_start
pam_start = libpam.pam_start
pam_start.restype = ctypes.c_int
pam_start.argtypes = [ctypes.c_char_p, ctypes.c_char_p, ctypes.POINTER(pam_conv), ctypes.POINTER(pam_handle_t)]
pam_authenticate = libpam.pam_authenticate
pam_authenticate.restype = ctypes.c_int
pam_authenticate.argtypes = [pam_handle_t, ctypes.c_int]
#Copies a string from one location in memory to another one, returns a pointer to it.
strdup = libc.strdup
strdup.restype = ctypes.POINTER(ctypes.c_char)
strdup.argstypes = [ctypes.c_char_p]
#Allocates memory for an array.
calloc = libc.calloc
calloc.restype = ctypes.c_void_p
calloc.argtypes = [ctypes.c_uint, ctypes.c_uint]
def pamAuth(username, password):
"""
Authenticate using pam and the login service.
Arguments are python strings of the clear-text username and password.
Returns a bool based on the result.
"""
@pam_conv_sig
def conv_function(num_msg, msg, resp, appdata_ptr):
"""
Does the "conversation" to try each message type and get the
answer from the user.
We'll just look for the one that doesn't echo anything and feed it
the correct password.
Needs to be in this scope to get the password, as we can't change
the function signature.
"""
#Address of a block of memory we're going to allocate for our response structs
resparr = calloc(num_msg, ctypes.sizeof(resp))
#Point resp at this memory
resp[0]= ctypes.cast(resparr, ctypes.POINTER(pam_response))
#Look for the message style that does not echo a prompt and requests
#just a password back. This is msg_style 1 according to the libpam headers.
for i in range(num_msg):
if msg[i][0].msg_style == 1:
pswd = strdup(str(password))
resp[i].contents.resp = ctypes.cast(pswd, ctypes.c_char_p)
resp[i].contents.resp_retcode = 0
return 0
#A handle for the blink structure pam_start fills in
pamh = pam_handle_t()
conv = pam_conv(conv_function, 0)
#Setup pam, get the response to the message we want.
#"login" is just the most basic pam auth stack. Could create a new
#file in /etc/pam.d and use the name of that instead of we wanted to.
pam_start("login", username, ctypes.pointer(conv),ctypes.pointer(pamh))
#Ask pam if that response results in a valid login.
res = pam_authenticate(pamh, 0)
#Turn that result into a bool to return.
return res == 0
#just a quick test to be sure it works
#not the right user/pass, but you could put them in to test...
if __name__ == "__main__":
print repr(pamAuth("ernstdu", "cats"))