Merge branch 'debian'
[hcoop/debian/courier-authlib.git] / authpam.c
1 /*
2 ** Copyright 1998 - 2012 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 "courierauthstaticlist.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
31 static const char *pam_username, *pam_password, *pam_service;
32
33 extern 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
42 static int pam_conv(int num_msg, const struct pam_message **msg,
43 struct pam_response **resp, void *appdata_ptr)
44 {
45 int i = 0;
46 struct 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
89 static struct pam_conv conv = {
90 pam_conv,
91 NULL
92 };
93
94 static int dopam(pam_handle_t **pamh, int *started)
95 {
96 int retval;
97
98 DPRINTF("pam_service=%s, pam_username=%s",
99 pam_service ? pam_service : "<null>",
100 pam_username ? pam_username : "<null>");
101
102 *started=1;
103
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);
108 *started=0;
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
160 struct callback_info {
161 int (*callback_func)(struct authinfo *, void *);
162 void *callback_arg;
163 } ;
164
165 static int callback_pam(struct authinfo *a, void *argptr)
166 {
167 struct callback_info *ci=(struct callback_info *)argptr;
168 pam_handle_t *pamh=NULL;
169 int pipefd[2];
170 int retval;
171 pid_t p;
172 int waitstat;
173 char *s;
174 char 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 {
225 int started;
226
227 close(pipefd[0]);
228 retval=dopam(&pamh, &started);
229 if (retval == PAM_SUCCESS)
230 if (write(pipefd[1], "", 1) < 0)
231 ; /* ignore gcc warning */
232 close(pipefd[1]);
233
234 if (started)
235 pam_end(pamh, retval);
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);
257 }
258
259 extern int auth_pam_pre(const char *userid, const char *service,
260 int (*callback)(struct authinfo *, void *),
261 void *arg);
262
263 int 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
291 static void auth_pam_cleanup()
292 {
293 }
294
295 static 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
305 struct authstaticinfo *courier_authpam_init()
306 {
307 return &authpam_info;
308 }