d9898ee8 |
1 | /* |
2 | ** Copyright 1998 - 2006 Double Precision, Inc. See COPYING for |
3 | ** distribution information. |
4 | */ |
5 | |
6 | #if HAVE_CONFIG_H |
7 | #include "courier_auth_config.h" |
8 | #endif |
9 | #include <stdio.h> |
10 | #include <stdlib.h> |
11 | #include <string.h> |
12 | #include <signal.h> |
13 | #include <errno.h> |
14 | #if HAVE_UNISTD_H |
15 | #include <unistd.h> |
16 | #endif |
17 | #include "auth.h" |
18 | #include "authwait.h" |
19 | #include "authstaticlist.h" |
20 | #include "courierauthdebug.h" |
21 | |
22 | #if HAVE_SECURITY_PAM_APPL_H |
23 | #include <security/pam_appl.h> |
24 | #endif |
25 | |
26 | #if HAVE_PAM_PAM_APPL_H |
27 | #include <Pam/pam_appl.h> |
28 | #endif |
29 | |
30 | static const char rcsid[]="$Id: authpam.c,v 1.24 2006/10/28 19:22:52 mrsam Exp $"; |
31 | |
32 | static const char *pam_username, *pam_password, *pam_service; |
33 | |
34 | extern void auth_pwd_enumerate( void(*cb_func)(const char *name, |
35 | uid_t uid, |
36 | gid_t gid, |
37 | const char *homedir, |
38 | const char *maildir, |
39 | const char *options, |
40 | void *void_arg), |
41 | void *void_arg); |
42 | |
43 | static int pam_conv(int num_msg, const struct pam_message **msg, |
44 | struct pam_response **resp, void *appdata_ptr) |
45 | { |
46 | int i = 0; |
47 | struct pam_response *repl = NULL; |
48 | |
49 | repl = malloc(sizeof(struct pam_response) * num_msg); |
50 | if (!repl) return PAM_CONV_ERR; |
51 | |
52 | for (i=0; i<num_msg; i++) |
53 | switch (msg[i]->msg_style) { |
54 | case PAM_PROMPT_ECHO_ON: |
55 | repl[i].resp_retcode = PAM_SUCCESS; |
56 | repl[i].resp = strdup(pam_username); |
57 | if (!repl[i].resp) |
58 | { |
59 | perror("strdup"); |
60 | exit(1); |
61 | } |
62 | break; |
63 | case PAM_PROMPT_ECHO_OFF: |
64 | repl[i].resp_retcode = PAM_SUCCESS; |
65 | repl[i].resp = strdup(pam_password); |
66 | if (!repl[i].resp) |
67 | { |
68 | perror("strdup"); |
69 | exit(1); |
70 | } |
71 | break; |
72 | case PAM_TEXT_INFO: |
73 | case PAM_ERROR_MSG: |
74 | if (write(2, msg[i]->msg, strlen(msg[i]->msg)) < 0 || |
75 | write(2, "\n", 1) < 0) |
76 | ; /* ignore gcc warning */ |
77 | |
78 | repl[i].resp_retcode = PAM_SUCCESS; |
79 | repl[i].resp = NULL; |
80 | break; |
81 | default: |
82 | free (repl); |
83 | return PAM_CONV_ERR; |
84 | } |
85 | |
86 | *resp=repl; |
87 | return PAM_SUCCESS; |
88 | } |
89 | |
90 | static struct pam_conv conv = { |
91 | pam_conv, |
92 | NULL |
93 | }; |
94 | |
95 | static int dopam(pam_handle_t **pamh) |
96 | { |
97 | int retval; |
98 | |
99 | DPRINTF("pam_service=%s, pam_username=%s", |
100 | pam_service ? pam_service : "<null>", |
101 | pam_username ? pam_username : "<null>"); |
102 | |
103 | retval=pam_start(pam_service, pam_username, &conv, pamh); |
104 | if (retval != PAM_SUCCESS) |
105 | { |
106 | DPRINTF("pam_start failed, result %d [Hint: bad PAM configuration?]", retval); |
107 | } |
108 | |
109 | #if 0 |
110 | if (retval == PAM_SUCCESS) |
111 | { |
112 | retval=pam_set_item(*pamh, PAM_AUTHTOK, pam_password); |
113 | if (retval != PAM_SUCCESS) |
114 | { |
115 | DPRINTF("pam_set_item failed, result %d", retval); |
116 | } |
117 | } |
118 | #endif |
119 | |
120 | if (retval == PAM_SUCCESS) |
121 | { |
122 | retval=pam_authenticate(*pamh, 0); |
123 | if (retval != PAM_SUCCESS) |
124 | { |
125 | DPRINTF("pam_authenticate failed, result %d", retval); |
126 | } |
127 | } |
128 | |
d9898ee8 |
129 | |
130 | #if HAVE_PAM_SETCRED |
d68dad8a |
131 | fprintf(stderr, "pam_setcred...\n"); |
d9898ee8 |
132 | if (retval == PAM_SUCCESS) |
133 | { |
134 | retval=pam_setcred(*pamh, PAM_ESTABLISH_CRED); |
135 | if (retval != PAM_SUCCESS) |
136 | { |
d68dad8a |
137 | fprintf(stderr, "pam_setcred failed, result %d\n", retval); |
d9898ee8 |
138 | } |
d68dad8a |
139 | fprintf(stderr, "pam_setcred done\n"); |
d9898ee8 |
140 | } |
141 | #endif |
d68dad8a |
142 | |
d9898ee8 |
143 | |
144 | if (retval == PAM_SUCCESS) |
145 | { |
146 | retval=pam_acct_mgmt(*pamh, 0); |
147 | if (retval != PAM_SUCCESS) |
148 | { |
149 | DPRINTF("pam_acct_mgmt failed, result %d", retval); |
150 | } |
151 | } |
152 | if (retval == PAM_SUCCESS) |
153 | { |
154 | DPRINTF("dopam successful"); |
155 | } |
156 | return (retval); |
157 | } |
158 | |
159 | struct callback_info { |
160 | int (*callback_func)(struct authinfo *, void *); |
161 | void *callback_arg; |
162 | } ; |
163 | |
164 | static int callback_pam(struct authinfo *a, void *argptr) |
165 | { |
166 | struct callback_info *ci=(struct callback_info *)argptr; |
167 | pam_handle_t *pamh=NULL; |
168 | int pipefd[2]; |
169 | int retval; |
170 | pid_t p; |
171 | int waitstat; |
172 | char *s; |
173 | char buf[1]; |
174 | |
175 | |
176 | a->clearpasswd=pam_password; |
177 | s=strdup(a->sysusername); |
178 | if (!s) |
179 | { |
180 | perror("malloc"); |
181 | return (1); |
182 | } |
183 | |
184 | |
185 | /* |
186 | ** OK, in order to transparently support PAM sessions inside this |
187 | ** authentication module, what we need to do is to fork(), and let |
188 | ** the child run in its parent place. Once the child process exits, |
189 | ** the parent calls pam_end_session, and clears the PAM library. |
190 | ** |
191 | ** This means that upon return from auth_pam(), your process ID |
192 | ** might've changed!!! |
193 | ** |
194 | ** However, if the authentication fails, we can simply exit, without |
195 | ** running as a child process. |
196 | ** |
197 | ** Additionally, the PAM library might allocate some resources that |
198 | ** authenticated clients should not access. Therefore, we fork |
199 | ** *before* we try to authenticate. If the authentication succeeds, |
200 | ** the child process will run in the parent's place. The child |
201 | ** process waits until the parent tells it whether the authentication |
202 | ** worked. If it worked, the child keeps running. If not, the child |
203 | ** exits, which the parent waits for. |
204 | ** |
205 | ** The authentication status is communicated to the child process via |
206 | ** a pipe. |
207 | */ |
208 | if (pipe(pipefd) < 0) |
209 | { |
210 | perror("pipe"); |
211 | free(s); |
212 | return (1); |
213 | } |
214 | |
215 | if ((p=fork()) == -1) |
216 | { |
217 | perror("fork"); |
218 | free(s); |
219 | return (1); |
220 | } |
221 | |
222 | if (p == 0) |
223 | { |
224 | close(pipefd[0]); |
225 | retval=dopam(&pamh); |
226 | if (retval == PAM_SUCCESS) |
227 | if (write(pipefd[1], "", 1) < 0) |
228 | ; /* ignore gcc warning */ |
229 | close(pipefd[1]); |
230 | _exit(0); |
231 | } |
232 | |
233 | |
234 | close(pipefd[1]); |
235 | while (wait(&waitstat) != p) |
236 | ; |
237 | if (read(pipefd[0], buf, 1) > 0) |
238 | { |
239 | int rc; |
240 | |
241 | close(pipefd[0]); |
242 | a->address=s; |
243 | rc=(*ci->callback_func)(a, ci->callback_arg); |
244 | free(s); |
245 | return rc; |
246 | } |
247 | close(pipefd[0]); |
248 | free(s); |
249 | errno=EPERM; |
250 | return (-1); |
251 | |
252 | #if 0 |
253 | free(s); |
254 | close(pipefd[0]); |
255 | |
256 | retval=dopam(&pamh); |
257 | |
258 | if (retval == PAM_SUCCESS) |
259 | retval=pam_open_session(pamh, 0); |
260 | |
261 | if (retval != PAM_SUCCESS) |
262 | { |
263 | if (pam_end(pamh, retval) != PAM_SUCCESS) |
264 | perror("Unable to release PAM tokens"); |
265 | |
266 | /* Wait for child to terminate */ |
267 | |
268 | close(pipefd[1]); /* Tell the child to shut down */ |
269 | while (wait(&waitstat) != p) |
270 | ; |
271 | return (-1); |
272 | } |
273 | |
274 | /* Tell child process to run in authenticated state */ |
275 | |
276 | write(pipefd[1], "", 1); |
277 | close(pipefd[1]); |
278 | |
279 | /* Wait for child process to finish */ |
280 | |
281 | while (wait(&waitstat) != p) |
282 | ; |
283 | |
284 | retval=pam_close_session(pamh, 0); |
285 | if (retval != PAM_SUCCESS) |
286 | perror("pam_close_session"); |
287 | |
288 | if (pam_end(pamh, retval) != PAM_SUCCESS) |
289 | perror("Unable to release PAM tokens"); |
290 | |
291 | if (WIFEXITED(waitstat)) |
292 | exit(WEXITSTATUS(waitstat)); |
293 | exit(255); |
294 | return (1); |
295 | #endif |
296 | } |
297 | |
298 | extern int auth_pam_pre(const char *userid, const char *service, |
299 | int (*callback)(struct authinfo *, void *), |
300 | void *arg); |
301 | |
302 | int auth_pam(const char *service, const char *type, char *authdata, |
303 | int (*callback_func)(struct authinfo *, void *), |
304 | void *callback_arg) |
305 | { |
306 | struct callback_info ci; |
307 | |
308 | if (strcmp(type, AUTHTYPE_LOGIN)) |
309 | { |
310 | DPRINTF("authpam only handles authtype=" AUTHTYPE_LOGIN); |
311 | errno=EPERM; |
312 | return (-1); |
313 | } |
314 | |
315 | if ((pam_username=strtok(authdata, "\n")) == 0 || |
316 | (pam_password=strtok(0, "\n")) == 0) |
317 | { |
318 | DPRINTF("incomplete username or missing password"); |
319 | errno=EPERM; |
320 | return (-1); |
321 | } |
322 | |
323 | pam_service=service; |
324 | |
325 | ci.callback_func=callback_func; |
326 | ci.callback_arg=callback_arg; |
327 | return auth_pam_pre(pam_username, service, &callback_pam, &ci); |
328 | } |
329 | |
330 | static void auth_pam_cleanup() |
331 | { |
332 | } |
333 | |
334 | static struct authstaticinfo authpam_info={ |
335 | "authpam", |
336 | auth_pam, |
337 | auth_pam_pre, |
338 | auth_pam_cleanup, |
339 | auth_syspasswd, |
340 | auth_pam_cleanup, |
341 | auth_pwd_enumerate}; |
342 | |
343 | |
344 | struct authstaticinfo *courier_authpam_init() |
345 | { |
346 | return &authpam_info; |
347 | } |