Commit | Line | Data |
---|---|---|
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 | |
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 | ||
b0322a85 | 94 | static 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 | ||
d9898ee8 | 131 | |
132 | #if HAVE_PAM_SETCRED | |
d68dad8a | 133 | fprintf(stderr, "pam_setcred...\n"); |
d9898ee8 | 134 | if (retval == PAM_SUCCESS) |
135 | { | |
136 | retval=pam_setcred(*pamh, PAM_ESTABLISH_CRED); | |
137 | if (retval != PAM_SUCCESS) | |
138 | { | |
d68dad8a | 139 | fprintf(stderr, "pam_setcred failed, result %d\n", retval); |
d9898ee8 | 140 | } |
d68dad8a | 141 | fprintf(stderr, "pam_setcred done\n"); |
d9898ee8 | 142 | } |
143 | #endif | |
d68dad8a | 144 | |
d9898ee8 | 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 | { | |
b0322a85 CE |
226 | int started; |
227 | ||
d9898ee8 | 228 | close(pipefd[0]); |
b0322a85 | 229 | retval=dopam(&pamh, &started); |
d9898ee8 | 230 | if (retval == PAM_SUCCESS) |
231 | if (write(pipefd[1], "", 1) < 0) | |
232 | ; /* ignore gcc warning */ | |
233 | close(pipefd[1]); | |
b0322a85 CE |
234 | |
235 | if (started) | |
236 | pam_end(pamh, retval); | |
d9898ee8 | 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); | |
d9898ee8 | 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 | } |