-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpam_redirector.c
267 lines (233 loc) · 6.56 KB
/
pam_redirector.c
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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
/*
* pam_redirector.c
*
* By Jamieson Becker, 09 May 2007
* Copyright (c) 2013, Jamieson Becker. GPL
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version. A copy of the GNU General Public
* License can be found at COPYING.
*
* CREDITS
* Benjamin Schieder for pam_extern on which this module is based
* An unknown RPI professor for client.c and U_client.c
*
* This PAM module is provides a simple client to a UNIX socket
* server (running on localhost) to request the socket server to authenticate
* the user with the following protocol:
*
* (This Pam Module sends:)
* username\n
* password\n
*
* and then expects:
* 0 (for success, user is ok)
* 1 (for failure, user login has failed.)
*
* The complete module includes a sample server (in Python) as well.
*
*/
#define PAM_SM_AUTH
#define PAM_SM_ACCOUNT
#define PAM_SM_SESSION
#define PAM_SM_PASSWORD
#include <security/pam_modules.h>
#include <security/pam_misc.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
// UNIX/IN sockets
#include <sys/socket.h>
#include <sys/un.h>
// #include <netinet/in.h>
// #include <netdb.h>
/* for security reasons, this version is basically restricted to local
* UNIX sockets. its faster anyway and if you know what you're doing you
* can write a local TCP redirector that is properly secured with SSL, or at
* least use stunnel.
*/
/*
* theory of operation
* this script gets called with argv[0] which is the name of the TCP socket to
* connect to. we connect to that socket and pass the username and password
* to the server. then we get a return value of either 0 (login ok) or non-zero
* (failure) which we return to PAM
*/
/* --- authentication management functions --- */
void *mymalloc(n){
void *c;
c = malloc(n);
if (c == NULL){ // Whoops, we didn't get the memory we desired
D((stderr, "Couldn't malloc %i bytes of memory", n));
exit(PAM_IGNORE);
}
return c;
}
void myfree(void *p){
_pam_overwrite(p);
_pam_drop(p);
}
PAM_EXTERN int
pam_sm_authenticate(pam_handle_t *pamh, int flags, int argc, const char **argv){
int i;
struct stat fstat;
const void *item;
char *username, *password;
username = password = NULL;
if (argv == NULL){
D(( "pam_redirector: no socket specified!" ));
return PAM_SUCCESS;
}
D(( "stat'ing %s\n", argv[0] ));
i=stat(argv[0], &fstat);
if (i != 0){
D(( "Couldn't stat %s\n", argv[0] ));
strerror(errno);
return PAM_IGNORE;
}
D(( "stat of socket: %i\n", i ));
// if (!S_ISREG(fstat.st_mode)){
// D(( "pam_redirector: %s is not a socket!\n", argv[0] ));
// return PAM_IGNORE;
// }
D(( "Getting username...\n" ));
i = pam_get_item(pamh, PAM_USER, &item);
if (i != PAM_SUCCESS) {
/* very strange. */
D(( "Couldn't get username? o_O\n" ));
return PAM_IGNORE;
} else if (item != NULL) { /* we have a user! */
username = mymalloc(strlen(item)+1);
sprintf(username, "%s", (char *)item);
item = NULL;
} else {
D(( "Username is empty? o_O\n" ));
return PAM_IGNORE; /* didn't work */
}
D(( "Getting password...\n" ));
i = pam_get_item(pamh, PAM_AUTHTOK, &item);
if (i != PAM_SUCCESS) {
/* very strange. */
D(( "Couldn't get password? o_O\n" ));
return PAM_IGNORE;
} else if (item != NULL) { /* we have a password! */
password = mymalloc(strlen(item)+1);
sprintf(password, "%s", (char *)item);
item = NULL;
} else {
D(( "We have no password\n" ));
password = mymalloc(2);
sprintf(password, " ");
}
// connect to UNIX stream
D(( "using %s for a socket\n", argv[0] ));
int sockfd, servlen, n;
struct sockaddr_un serv_addr;
bzero((char *)&serv_addr,sizeof(serv_addr));
serv_addr.sun_family = AF_UNIX;
strcpy(serv_addr.sun_path, argv[0]);
servlen = strlen(serv_addr.sun_path) +
sizeof(serv_addr.sun_family);
if ((sockfd = socket(AF_UNIX, SOCK_STREAM,0)) < 0)
D(("Creating socket"));
if (connect(sockfd, (struct sockaddr *)
&serv_addr, servlen) < 0)
D(("Connecting"));
// we already have username and password, so..
D(("sending username %s", username));
n = write(sockfd,username,strlen(username));
write(sockfd,"\n",(1));
if (n < 0) {
D(("ERROR writing username to socket"));
return PAM_IGNORE;
}
D(("sending password %s", password));
n = write(sockfd,password,strlen(password));
write(sockfd,"\n",(1));
if (n < 0) {
D(("ERROR writing password to socket"));
return PAM_IGNORE;
}
// ok, we're done sending
D(("preparing response buffer"));
// prepare response buffer
char *response;
response=(char *)mymalloc(5); // should be enough to hold all return values (0-127)
memset(response, 0, 5);
D(("ok, get response."));
n = read(sockfd,response,4); // one less than malloc'd
D(("ok, got response."));
if (n < 0) {
D(("ERROR reading response from socket"));
return PAM_IGNORE;
}
D(("close sockfd."));
// done receiving the response code
close(sockfd); // close socket
D(("response to integer."));
i=atoi(response);
D(( "done. Return code: %i", i ));
// At this point, username and password are malloc'd, so no need to check before free
myfree(username);
myfree(password);
if (i==0) {
D(( "Returning PAM_SUCCESS" ));
return PAM_SUCCESS;
} else {
D(( "Returning PAM_IGNORE" ));
return PAM_IGNORE;
}
}
PAM_EXTERN int
pam_sm_setcred(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
return PAM_SUCCESS;
}
/* --- password management --- */
PAM_EXTERN int
pam_sm_chauthtok(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
return PAM_SUCCESS;
}
/* --- account management functions --- */
PAM_EXTERN int
pam_sm_acct_mgmt(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
return PAM_SUCCESS;
}
/* --- session management --- */
PAM_EXTERN int
pam_sm_open_session(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
return PAM_SUCCESS;
}
PAM_EXTERN int
pam_sm_close_session(pam_handle_t *pamh, int flags,
int argc, const char **argv)
{
return PAM_SUCCESS;
}
/* end of module definition */
/* static module data */
#ifdef PAM_STATIC
struct pam_module _pam_redirector_modstruct = {
"pam_redirector",
pam_sm_authenticate,
pam_sm_chauthtok,
NULL,
NULL,
NULL,
NULL
};
#endif