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