Build courier-authlib 0.60.2-0hcoop3.
[hcoop/debian/courier-authlib.git] / authpam.c
CommitLineData
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
30static const char rcsid[]="$Id: authpam.c,v 1.24 2006/10/28 19:22:52 mrsam Exp $";
31
32static const char *pam_username, *pam_password, *pam_service;
33
34extern 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
43static int pam_conv(int num_msg, const struct pam_message **msg,
44 struct pam_response **resp, void *appdata_ptr)
45{
46int i = 0;
47struct 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
90static struct pam_conv conv = {
91 pam_conv,
92 NULL
93 };
94
95static int dopam(pam_handle_t **pamh)
96{
97int 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
159struct callback_info {
160 int (*callback_func)(struct authinfo *, void *);
161 void *callback_arg;
162 } ;
163
164static int callback_pam(struct authinfo *a, void *argptr)
165{
166struct callback_info *ci=(struct callback_info *)argptr;
167pam_handle_t *pamh=NULL;
168int pipefd[2];
169int retval;
170pid_t p;
171int waitstat;
172char *s;
173char 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
298extern int auth_pam_pre(const char *userid, const char *service,
299 int (*callback)(struct authinfo *, void *),
300 void *arg);
301
302int 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
330static void auth_pam_cleanup()
331{
332}
333
334static 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
344struct authstaticinfo *courier_authpam_init()
345{
346 return &authpam_info;
347}