Imported upstream version 0.60.2
[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
129#if 0
130
131#if HAVE_PAM_SETCRED
132 if (retval == PAM_SUCCESS)
133 {
134 retval=pam_setcred(*pamh, PAM_ESTABLISH_CRED);
135 if (retval != PAM_SUCCESS)
136 {
137 DPRINTF("pam_setcred failed, result %d", retval);
138 }
139 }
140#endif
141#endif
142
143 if (retval == PAM_SUCCESS)
144 {
145 retval=pam_acct_mgmt(*pamh, 0);
146 if (retval != PAM_SUCCESS)
147 {
148 DPRINTF("pam_acct_mgmt failed, result %d", retval);
149 }
150 }
151 if (retval == PAM_SUCCESS)
152 {
153 DPRINTF("dopam successful");
154 }
155 return (retval);
156}
157
158struct callback_info {
159 int (*callback_func)(struct authinfo *, void *);
160 void *callback_arg;
161 } ;
162
163static int callback_pam(struct authinfo *a, void *argptr)
164{
165struct callback_info *ci=(struct callback_info *)argptr;
166pam_handle_t *pamh=NULL;
167int pipefd[2];
168int retval;
169pid_t p;
170int waitstat;
171char *s;
172char buf[1];
173
174
175 a->clearpasswd=pam_password;
176 s=strdup(a->sysusername);
177 if (!s)
178 {
179 perror("malloc");
180 return (1);
181 }
182
183
184/*
185** OK, in order to transparently support PAM sessions inside this
186** authentication module, what we need to do is to fork(), and let
187** the child run in its parent place. Once the child process exits,
188** the parent calls pam_end_session, and clears the PAM library.
189**
190** This means that upon return from auth_pam(), your process ID
191** might've changed!!!
192**
193** However, if the authentication fails, we can simply exit, without
194** running as a child process.
195**
196** Additionally, the PAM library might allocate some resources that
197** authenticated clients should not access. Therefore, we fork
198** *before* we try to authenticate. If the authentication succeeds,
199** the child process will run in the parent's place. The child
200** process waits until the parent tells it whether the authentication
201** worked. If it worked, the child keeps running. If not, the child
202** exits, which the parent waits for.
203**
204** The authentication status is communicated to the child process via
205** a pipe.
206*/
207 if (pipe(pipefd) < 0)
208 {
209 perror("pipe");
210 free(s);
211 return (1);
212 }
213
214 if ((p=fork()) == -1)
215 {
216 perror("fork");
217 free(s);
218 return (1);
219 }
220
221 if (p == 0)
222 {
223 close(pipefd[0]);
224 retval=dopam(&pamh);
225 if (retval == PAM_SUCCESS)
226 if (write(pipefd[1], "", 1) < 0)
227 ; /* ignore gcc warning */
228 close(pipefd[1]);
229 _exit(0);
230 }
231
232
233 close(pipefd[1]);
234 while (wait(&waitstat) != p)
235 ;
236 if (read(pipefd[0], buf, 1) > 0)
237 {
238 int rc;
239
240 close(pipefd[0]);
241 a->address=s;
242 rc=(*ci->callback_func)(a, ci->callback_arg);
243 free(s);
244 return rc;
245 }
246 close(pipefd[0]);
247 free(s);
248 errno=EPERM;
249 return (-1);
250
251#if 0
252 free(s);
253 close(pipefd[0]);
254
255 retval=dopam(&pamh);
256
257 if (retval == PAM_SUCCESS)
258 retval=pam_open_session(pamh, 0);
259
260 if (retval != PAM_SUCCESS)
261 {
262 if (pam_end(pamh, retval) != PAM_SUCCESS)
263 perror("Unable to release PAM tokens");
264
265 /* Wait for child to terminate */
266
267 close(pipefd[1]); /* Tell the child to shut down */
268 while (wait(&waitstat) != p)
269 ;
270 return (-1);
271 }
272
273 /* Tell child process to run in authenticated state */
274
275 write(pipefd[1], "", 1);
276 close(pipefd[1]);
277
278 /* Wait for child process to finish */
279
280 while (wait(&waitstat) != p)
281 ;
282
283 retval=pam_close_session(pamh, 0);
284 if (retval != PAM_SUCCESS)
285 perror("pam_close_session");
286
287 if (pam_end(pamh, retval) != PAM_SUCCESS)
288 perror("Unable to release PAM tokens");
289
290 if (WIFEXITED(waitstat))
291 exit(WEXITSTATUS(waitstat));
292 exit(255);
293 return (1);
294#endif
295}
296
297extern int auth_pam_pre(const char *userid, const char *service,
298 int (*callback)(struct authinfo *, void *),
299 void *arg);
300
301int auth_pam(const char *service, const char *type, char *authdata,
302 int (*callback_func)(struct authinfo *, void *),
303 void *callback_arg)
304{
305 struct callback_info ci;
306
307 if (strcmp(type, AUTHTYPE_LOGIN))
308 {
309 DPRINTF("authpam only handles authtype=" AUTHTYPE_LOGIN);
310 errno=EPERM;
311 return (-1);
312 }
313
314 if ((pam_username=strtok(authdata, "\n")) == 0 ||
315 (pam_password=strtok(0, "\n")) == 0)
316 {
317 DPRINTF("incomplete username or missing password");
318 errno=EPERM;
319 return (-1);
320 }
321
322 pam_service=service;
323
324 ci.callback_func=callback_func;
325 ci.callback_arg=callback_arg;
326 return auth_pam_pre(pam_username, service, &callback_pam, &ci);
327}
328
329static void auth_pam_cleanup()
330{
331}
332
333static struct authstaticinfo authpam_info={
334 "authpam",
335 auth_pam,
336 auth_pam_pre,
337 auth_pam_cleanup,
338 auth_syspasswd,
339 auth_pam_cleanup,
340 auth_pwd_enumerate};
341
342
343struct authstaticinfo *courier_authpam_init()
344{
345 return &authpam_info;
346}