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