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