Merge branch 'upstream'
[hcoop/debian/courier-authlib.git] / authpam.c
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
129
130 #if HAVE_PAM_SETCRED
131 fprintf(stderr, "pam_setcred...\n");
132 if (retval == PAM_SUCCESS)
133 {
134 retval=pam_setcred(*pamh, PAM_ESTABLISH_CRED);
135 if (retval != PAM_SUCCESS)
136 {
137 fprintf(stderr, "pam_setcred failed, result %d\n", retval);
138 }
139 fprintf(stderr, "pam_setcred done\n");
140 }
141 #endif
142
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 }