|
1 # |
|
2 # Make pam_set_data/pam_get_data work with OpenSSH |
|
3 # |
|
4 # The way PAM is implemented in OpenSSH makes pam_set_data unusable |
|
5 # for passing data between PAM stacks. |
|
6 # |
|
7 # The problem is, that pam_authenticate and pam_acct_mgmt are called |
|
8 # in a separate auxiliary process. Any data stored using pam_set_data |
|
9 # and any other state information stored by those two functions are |
|
10 # lost when the auxiliary process exits (with exceptions like |
|
11 # environment variables, which are sent over between the processes). |
|
12 # |
|
13 # This patch fixes this by switching the roles of the monitor and the |
|
14 # auxiliary process when doing PAM authentication. In the new code the |
|
15 # monitor will be the one calling pam_authenticate and pam_acct_mgmt |
|
16 # (eventually blocking and calling callbacks), whereas the other |
|
17 # process (callback child) will be sending messages to the client |
|
18 # (either directly or through privsep child). |
|
19 # |
|
20 # Patch origin: in-house |
|
21 # |
|
22 # Reported upstream: |
|
23 # https://bugzilla.mindrot.org/show_bug.cgi?id=2548 |
|
24 # |
|
25 |
|
26 diff -pur old/auth-pam.c new/auth-pam.c |
|
27 --- old/auth-pam.c |
|
28 +++ new/auth-pam.c |
|
29 @@ -97,6 +97,7 @@ |
|
30 #include "ssh-gss.h" |
|
31 #endif |
|
32 #include "monitor_wrap.h" |
|
33 +#include "ssherr.h" |
|
34 |
|
35 extern ServerOptions options; |
|
36 extern Buffer loginmsg; |
|
37 @@ -109,38 +110,26 @@ extern u_int utmp_len; |
|
38 #endif |
|
39 |
|
40 /* |
|
41 - * Formerly known as USE_POSIX_THREADS, using this is completely unsupported |
|
42 - * and generally a bad idea. Use at own risk and do not expect support if |
|
43 - * this breaks. |
|
44 + * PAM processing model has been rewritten. |
|
45 + * Now all the calls to PAM are within the monitor process, |
|
46 + * pam_get_data/pam_set_data works as designed and there is no need |
|
47 + * for the threads anymore. |
|
48 */ |
|
49 #ifdef UNSUPPORTED_POSIX_THREADS_HACK |
|
50 -#include <pthread.h> |
|
51 -/* |
|
52 - * Avoid namespace clash when *not* using pthreads for systems *with* |
|
53 - * pthreads, which unconditionally define pthread_t via sys/types.h |
|
54 - * (e.g. Linux) |
|
55 - */ |
|
56 -typedef pthread_t sp_pthread_t; |
|
57 -#else |
|
58 -typedef pid_t sp_pthread_t; |
|
59 +# error "UNSUPPORTED_POSIX_THREADS_HACK no longer supported" |
|
60 #endif |
|
61 |
|
62 struct pam_ctxt { |
|
63 - sp_pthread_t pam_thread; |
|
64 - int pam_psock; |
|
65 - int pam_csock; |
|
66 - int pam_done; |
|
67 + pid_t pam_child; |
|
68 + int pam_psock; |
|
69 + int pam_csock; |
|
70 + int pam_done; |
|
71 }; |
|
72 |
|
73 static void sshpam_free_ctx(void *); |
|
74 static struct pam_ctxt *cleanup_ctxt; |
|
75 |
|
76 -#ifndef UNSUPPORTED_POSIX_THREADS_HACK |
|
77 -/* |
|
78 - * Simulate threads with processes. |
|
79 - */ |
|
80 - |
|
81 -static int sshpam_thread_status = -1; |
|
82 +static int sshpam_child_status = -1; |
|
83 static mysig_t sshpam_oldsig; |
|
84 |
|
85 static void |
|
86 @@ -149,78 +138,22 @@ sshpam_sigchld_handler(int sig) |
|
87 signal(SIGCHLD, SIG_DFL); |
|
88 if (cleanup_ctxt == NULL) |
|
89 return; /* handler called after PAM cleanup, shouldn't happen */ |
|
90 - if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, WNOHANG) |
|
91 + if (waitpid(cleanup_ctxt->pam_child, &sshpam_child_status, WNOHANG) |
|
92 <= 0) { |
|
93 - /* PAM thread has not exitted, privsep slave must have */ |
|
94 - kill(cleanup_ctxt->pam_thread, SIGTERM); |
|
95 - if (waitpid(cleanup_ctxt->pam_thread, &sshpam_thread_status, 0) |
|
96 + /* callback child has not exited, privsep slave must have */ |
|
97 + kill(cleanup_ctxt->pam_child, SIGTERM); |
|
98 + if (waitpid(cleanup_ctxt->pam_child, &sshpam_child_status, 0) |
|
99 <= 0) |
|
100 return; /* could not wait */ |
|
101 } |
|
102 - if (WIFSIGNALED(sshpam_thread_status) && |
|
103 - WTERMSIG(sshpam_thread_status) == SIGTERM) |
|
104 - return; /* terminated by pthread_cancel */ |
|
105 - if (!WIFEXITED(sshpam_thread_status)) |
|
106 - sigdie("PAM: authentication thread exited unexpectedly"); |
|
107 - if (WEXITSTATUS(sshpam_thread_status) != 0) |
|
108 - sigdie("PAM: authentication thread exited uncleanly"); |
|
109 -} |
|
110 - |
|
111 -/* ARGSUSED */ |
|
112 -static void |
|
113 -pthread_exit(void *value) |
|
114 -{ |
|
115 - _exit(0); |
|
116 -} |
|
117 - |
|
118 -/* ARGSUSED */ |
|
119 -static int |
|
120 -pthread_create(sp_pthread_t *thread, const void *attr, |
|
121 - void *(*thread_start)(void *), void *arg) |
|
122 -{ |
|
123 - pid_t pid; |
|
124 - struct pam_ctxt *ctx = arg; |
|
125 - |
|
126 - sshpam_thread_status = -1; |
|
127 - switch ((pid = fork())) { |
|
128 - case -1: |
|
129 - error("fork(): %s", strerror(errno)); |
|
130 - return (-1); |
|
131 - case 0: |
|
132 - close(ctx->pam_psock); |
|
133 - ctx->pam_psock = -1; |
|
134 - thread_start(arg); |
|
135 - _exit(1); |
|
136 - default: |
|
137 - *thread = pid; |
|
138 - close(ctx->pam_csock); |
|
139 - ctx->pam_csock = -1; |
|
140 - sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler); |
|
141 - return (0); |
|
142 - } |
|
143 -} |
|
144 - |
|
145 -static int |
|
146 -pthread_cancel(sp_pthread_t thread) |
|
147 -{ |
|
148 - signal(SIGCHLD, sshpam_oldsig); |
|
149 - return (kill(thread, SIGTERM)); |
|
150 -} |
|
151 - |
|
152 -/* ARGSUSED */ |
|
153 -static int |
|
154 -pthread_join(sp_pthread_t thread, void **value) |
|
155 -{ |
|
156 - int status; |
|
157 - |
|
158 - if (sshpam_thread_status != -1) |
|
159 - return (sshpam_thread_status); |
|
160 - signal(SIGCHLD, sshpam_oldsig); |
|
161 - waitpid(thread, &status, 0); |
|
162 - return (status); |
|
163 + if (WIFSIGNALED(sshpam_child_status) && |
|
164 + WTERMSIG(sshpam_child_status) == SIGTERM) |
|
165 + return; |
|
166 + if (!WIFEXITED(sshpam_child_status)) |
|
167 + sigdie("PAM: callback child exited unexpectedly"); |
|
168 + if (WEXITSTATUS(sshpam_child_status) != 0) |
|
169 + sigdie("PAM: callback child exited uncleanly"); |
|
170 } |
|
171 -#endif |
|
172 - |
|
173 |
|
174 static pam_handle_t *sshpam_handle = NULL; |
|
175 static int sshpam_err = 0; |
|
176 @@ -290,55 +223,11 @@ sshpam_password_change_required(int reqd |
|
177 } |
|
178 } |
|
179 |
|
180 -/* Import regular and PAM environment from subprocess */ |
|
181 -static void |
|
182 -import_environments(Buffer *b) |
|
183 -{ |
|
184 - char *env; |
|
185 - u_int i, num_env; |
|
186 - int err; |
|
187 - |
|
188 - debug3("PAM: %s entering", __func__); |
|
189 - |
|
190 -#ifndef UNSUPPORTED_POSIX_THREADS_HACK |
|
191 - /* Import variables set by do_pam_account */ |
|
192 - sshpam_account_status = buffer_get_int(b); |
|
193 - sshpam_password_change_required(buffer_get_int(b)); |
|
194 - |
|
195 - /* Import environment from subprocess */ |
|
196 - num_env = buffer_get_int(b); |
|
197 - if (num_env > 1024) |
|
198 - fatal("%s: received %u environment variables, expected <= 1024", |
|
199 - __func__, num_env); |
|
200 - sshpam_env = xcalloc(num_env + 1, sizeof(*sshpam_env)); |
|
201 - debug3("PAM: num env strings %d", num_env); |
|
202 - for(i = 0; i < num_env; i++) |
|
203 - sshpam_env[i] = buffer_get_string(b, NULL); |
|
204 - |
|
205 - sshpam_env[num_env] = NULL; |
|
206 - |
|
207 - /* Import PAM environment from subprocess */ |
|
208 - num_env = buffer_get_int(b); |
|
209 - debug("PAM: num PAM env strings %d", num_env); |
|
210 - for(i = 0; i < num_env; i++) { |
|
211 - env = buffer_get_string(b, NULL); |
|
212 - |
|
213 -#ifdef HAVE_PAM_PUTENV |
|
214 - /* Errors are not fatal here */ |
|
215 - if ((err = pam_putenv(sshpam_handle, env)) != PAM_SUCCESS) { |
|
216 - error("PAM: pam_putenv: %s", |
|
217 - pam_strerror(sshpam_handle, sshpam_err)); |
|
218 - } |
|
219 -#endif |
|
220 - } |
|
221 -#endif |
|
222 -} |
|
223 - |
|
224 /* |
|
225 - * Conversation function for authentication thread. |
|
226 + * Conversation function for keyboard-interactive authentication. |
|
227 */ |
|
228 static int |
|
229 -sshpam_thread_conv(int n, sshpam_const struct pam_message **msg, |
|
230 +sshpam_child_conv(int n, sshpam_const struct pam_message **msg, |
|
231 struct pam_response **resp, void *data) |
|
232 { |
|
233 Buffer buffer; |
|
234 @@ -420,48 +309,84 @@ sshpam_thread_conv(int n, sshpam_const s |
|
235 } |
|
236 |
|
237 /* |
|
238 - * Authentication thread. |
|
239 + * Terminates the call back child. |
|
240 + * |
|
241 + * Sends a message of type PAM_SUCCESS or PAM_AUTH_ERR to the child. |
|
242 + * In response receives a message with remaining PAM prompts. |
|
243 + * When not using privilege separation, receives serialized packet state too. |
|
244 + * |
|
245 + * After that, the child exits. |
|
246 */ |
|
247 -static void * |
|
248 -sshpam_thread(void *ctxtp) |
|
249 +void |
|
250 +relieve_from_duty(struct pam_ctxt *ctxt) |
|
251 { |
|
252 - struct pam_ctxt *ctxt = ctxtp; |
|
253 Buffer buffer; |
|
254 - struct pam_conv sshpam_conv; |
|
255 - int flags = (options.permit_empty_passwd == 0 ? |
|
256 - PAM_DISALLOW_NULL_AUTHTOK : 0); |
|
257 -#ifndef UNSUPPORTED_POSIX_THREADS_HACK |
|
258 - extern char **environ; |
|
259 - char **env_from_pam; |
|
260 - u_int i; |
|
261 - const char *pam_user; |
|
262 - const char **ptr_pam_user = &pam_user; |
|
263 - char *tz = getenv("TZ"); |
|
264 + struct ssh *ssh = active_state; |
|
265 + int r; |
|
266 + u_char type; |
|
267 + char *msg; |
|
268 + u_int len; |
|
269 |
|
270 - sshpam_err = pam_get_item(sshpam_handle, PAM_USER, |
|
271 - (sshpam_const void **)ptr_pam_user); |
|
272 - if (sshpam_err != PAM_SUCCESS) |
|
273 - goto auth_fail; |
|
274 + buffer_init(&buffer); |
|
275 + buffer_put_cstring(&buffer, "OK"); |
|
276 + type = (ctxt->pam_done == 1) ? PAM_SUCCESS : PAM_AUTH_ERR; |
|
277 + if (ssh_msg_send(ctxt->pam_csock, type, &buffer) == -1) { |
|
278 + buffer_free(&buffer); |
|
279 + fatal("%s: cannnot terminate callback child (send)", __func__); |
|
280 + } |
|
281 |
|
282 - environ[0] = NULL; |
|
283 - if (tz != NULL) |
|
284 - if (setenv("TZ", tz, 1) == -1) |
|
285 - error("PAM: could not set TZ environment: %s", |
|
286 - strerror(errno)); |
|
287 - |
|
288 - if (sshpam_authctxt != NULL) { |
|
289 - setproctitle("%s [pam]", |
|
290 - sshpam_authctxt->valid ? pam_user : "unknown"); |
|
291 + buffer_clear(&buffer); |
|
292 + if (ssh_msg_recv(ctxt->pam_csock, &buffer) == -1) { |
|
293 + buffer_free(&buffer); |
|
294 + fatal("%s: cannnot terminate callback child (receive)", |
|
295 + __func__); |
|
296 } |
|
297 -#endif |
|
298 + type = buffer_get_char(&buffer); |
|
299 + msg = buffer_get_cstring(&buffer, &len); |
|
300 + if (len) |
|
301 + buffer_append(&loginmsg, msg, len); |
|
302 + /* if not using privsep child, sync packet state from callback child */ |
|
303 + if (!use_privsep) { |
|
304 + if ((r = ssh_packet_set_state(ssh, &buffer)) != 0) |
|
305 + fatal("%s: set_state failed: %s", |
|
306 + __func__, ssh_err(r)); |
|
307 + } |
|
308 + free(msg); |
|
309 + buffer_free(&buffer); |
|
310 + close(ctxt->pam_csock); |
|
311 + ctxt->pam_csock = -1; |
|
312 +} |
|
313 + |
|
314 +int |
|
315 +get_pam_done(void *ctxt) |
|
316 +{ |
|
317 + struct pam_ctxt *pctxt = (struct pam_ctxt *)ctxt; |
|
318 + return (pctxt->pam_done); |
|
319 +} |
|
320 |
|
321 - sshpam_conv.conv = sshpam_thread_conv; |
|
322 +/* |
|
323 + * Perform PAM authentication. |
|
324 + * |
|
325 + * PAM APIs (pam_authenticate, pam_acct_mgmt, ...) block and call the |
|
326 + * provided callback conversation function (sshpam_conv). The conversation |
|
327 + * function sends messages to the callback child (pam_ctxt.pam_child), which |
|
328 + * communicates with the client directly, or indirectly through privsep child. |
|
329 + */ |
|
330 +void |
|
331 +do_pam_auth(struct pam_ctxt *ctxt) |
|
332 +{ |
|
333 + struct pam_conv sshpam_conv; |
|
334 + int flags = (options.permit_empty_passwd == 0 ? |
|
335 + PAM_DISALLOW_NULL_AUTHTOK : 0); |
|
336 + |
|
337 + sshpam_conv.conv = sshpam_child_conv; |
|
338 sshpam_conv.appdata_ptr = ctxt; |
|
339 |
|
340 + ctxt->pam_done = -1; |
|
341 + |
|
342 if (sshpam_authctxt == NULL) |
|
343 fatal("%s: PAM authctxt not initialized", __func__); |
|
344 |
|
345 - buffer_init(&buffer); |
|
346 sshpam_err = pam_set_item(sshpam_handle, PAM_CONV, |
|
347 (const void *)&sshpam_conv); |
|
348 if (sshpam_err != PAM_SUCCESS) |
|
349 @@ -484,60 +409,34 @@ sshpam_thread(void *ctxtp) |
|
350 } |
|
351 } |
|
352 |
|
353 - buffer_put_cstring(&buffer, "OK"); |
|
354 - |
|
355 -#ifndef UNSUPPORTED_POSIX_THREADS_HACK |
|
356 - /* Export variables set by do_pam_account */ |
|
357 - buffer_put_int(&buffer, sshpam_account_status); |
|
358 - buffer_put_int(&buffer, sshpam_authctxt->force_pwchange); |
|
359 - |
|
360 - /* Export any environment strings set in child */ |
|
361 - for(i = 0; environ[i] != NULL; i++) |
|
362 - ; /* Count */ |
|
363 - buffer_put_int(&buffer, i); |
|
364 - for(i = 0; environ[i] != NULL; i++) |
|
365 - buffer_put_cstring(&buffer, environ[i]); |
|
366 - |
|
367 - /* Export any environment strings set by PAM in child */ |
|
368 - env_from_pam = pam_getenvlist(sshpam_handle); |
|
369 - for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) |
|
370 - ; /* Count */ |
|
371 - buffer_put_int(&buffer, i); |
|
372 - for(i = 0; env_from_pam != NULL && env_from_pam[i] != NULL; i++) |
|
373 - buffer_put_cstring(&buffer, env_from_pam[i]); |
|
374 -#endif /* UNSUPPORTED_POSIX_THREADS_HACK */ |
|
375 - |
|
376 - /* XXX - can't do much about an error here */ |
|
377 - ssh_msg_send(ctxt->pam_csock, sshpam_err, &buffer); |
|
378 - buffer_free(&buffer); |
|
379 - pthread_exit(NULL); |
|
380 + ctxt->pam_done = 1; |
|
381 |
|
382 auth_fail: |
|
383 - buffer_put_cstring(&buffer, |
|
384 - pam_strerror(sshpam_handle, sshpam_err)); |
|
385 - /* XXX - can't do much about an error here */ |
|
386 - if (sshpam_err == PAM_ACCT_EXPIRED) |
|
387 - ssh_msg_send(ctxt->pam_csock, PAM_ACCT_EXPIRED, &buffer); |
|
388 - else |
|
389 - ssh_msg_send(ctxt->pam_csock, PAM_AUTH_ERR, &buffer); |
|
390 - buffer_free(&buffer); |
|
391 - pthread_exit(NULL); |
|
392 - |
|
393 - return (NULL); /* Avoid warning for non-pthread case */ |
|
394 + if (sshpam_err != PAM_SUCCESS) |
|
395 + error("PAM: %s for %s%.100s from %.100s", |
|
396 + pam_strerror(sshpam_handle, sshpam_err), |
|
397 + sshpam_authctxt->valid ? "" : "illegal user ", |
|
398 + sshpam_authctxt->user, |
|
399 + get_remote_name_or_ip(utmp_len, options.use_dns)); |
|
400 + relieve_from_duty(ctxt); |
|
401 } |
|
402 |
|
403 void |
|
404 -sshpam_thread_cleanup(void) |
|
405 +sshpam_child_cleanup(void) |
|
406 { |
|
407 struct pam_ctxt *ctxt = cleanup_ctxt; |
|
408 |
|
409 debug3("PAM: %s entering", __func__); |
|
410 - if (ctxt != NULL && ctxt->pam_thread != 0) { |
|
411 - pthread_cancel(ctxt->pam_thread); |
|
412 - pthread_join(ctxt->pam_thread, NULL); |
|
413 - close(ctxt->pam_psock); |
|
414 - close(ctxt->pam_csock); |
|
415 - memset(ctxt, 0, sizeof(*ctxt)); |
|
416 + if (ctxt != NULL && ctxt->pam_child != 0) { |
|
417 + signal(SIGCHLD, sshpam_oldsig); |
|
418 + /* callback child should have had exited by now */ |
|
419 + kill(ctxt->pam_child, SIGTERM); |
|
420 + if (ctxt->pam_psock != -1) |
|
421 + close(ctxt->pam_psock); |
|
422 + if (ctxt->pam_csock != -1) |
|
423 + close(ctxt->pam_csock); |
|
424 + if (sshpam_child_status == -1) |
|
425 + waitpid(ctxt->pam_child, &sshpam_child_status, 0); |
|
426 cleanup_ctxt = NULL; |
|
427 } |
|
428 } |
|
429 @@ -686,7 +585,6 @@ derive_pam_service_name(Authctxt *authct |
|
430 static int |
|
431 sshpam_init(Authctxt *authctxt) |
|
432 { |
|
433 - extern char *__progname; |
|
434 const char *pam_rhost, *pam_user, *user = authctxt->user; |
|
435 const char **ptr_pam_user = &pam_user; |
|
436 |
|
437 @@ -792,6 +690,7 @@ sshpam_init_ctx(Authctxt *authctxt) |
|
438 { |
|
439 struct pam_ctxt *ctxt; |
|
440 int socks[2]; |
|
441 + pid_t pid; |
|
442 |
|
443 debug3("PAM: %s entering", __func__); |
|
444 /* |
|
445 @@ -809,7 +708,7 @@ sshpam_init_ctx(Authctxt *authctxt) |
|
446 |
|
447 ctxt = xcalloc(1, sizeof *ctxt); |
|
448 |
|
449 - /* Start the authentication thread */ |
|
450 + /* Fork the callback child and start PAM authentication */ |
|
451 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, socks) == -1) { |
|
452 error("PAM: failed create sockets: %s", strerror(errno)); |
|
453 free(ctxt); |
|
454 @@ -817,15 +716,29 @@ sshpam_init_ctx(Authctxt *authctxt) |
|
455 } |
|
456 ctxt->pam_psock = socks[0]; |
|
457 ctxt->pam_csock = socks[1]; |
|
458 - if (pthread_create(&ctxt->pam_thread, NULL, sshpam_thread, ctxt) == -1) { |
|
459 - error("PAM: failed to start authentication thread: %s", |
|
460 - strerror(errno)); |
|
461 + |
|
462 + sshpam_child_status = -1; |
|
463 + switch ((pid = fork())) { |
|
464 + case -1: |
|
465 + error("fork(): %s", strerror(errno)); |
|
466 close(socks[0]); |
|
467 close(socks[1]); |
|
468 free(ctxt); |
|
469 return (NULL); |
|
470 + case 0: |
|
471 + /* child processes query & respond for kbdint */ |
|
472 + close(ctxt->pam_csock); |
|
473 + ctxt->pam_csock = -1; |
|
474 + break; |
|
475 + default: |
|
476 + /* parent does PAM */ |
|
477 + ctxt->pam_child = pid; |
|
478 + close(ctxt->pam_psock); |
|
479 + ctxt->pam_psock = -1; |
|
480 + sshpam_oldsig = signal(SIGCHLD, sshpam_sigchld_handler); |
|
481 + cleanup_ctxt = ctxt; |
|
482 + do_pam_auth(ctxt); |
|
483 } |
|
484 - cleanup_ctxt = ctxt; |
|
485 return (ctxt); |
|
486 } |
|
487 |
|
488 @@ -839,8 +752,11 @@ sshpam_query(void *ctx, char **name, cha |
|
489 u_char type; |
|
490 char *msg; |
|
491 size_t len, mlen; |
|
492 + struct ssh *ssh; |
|
493 + int r; |
|
494 |
|
495 debug3("PAM: %s entering", __func__); |
|
496 + |
|
497 buffer_init(&buffer); |
|
498 *name = xstrdup(""); |
|
499 *info = xstrdup(""); |
|
500 @@ -848,6 +764,17 @@ sshpam_query(void *ctx, char **name, cha |
|
501 **prompts = NULL; |
|
502 plen = 0; |
|
503 *echo_on = xmalloc(sizeof(u_int)); |
|
504 + |
|
505 + /* in case PAM was already done in callback child */ |
|
506 + switch (ctxt->pam_done) { |
|
507 + case 1: |
|
508 + return (0); |
|
509 + case 0: |
|
510 + break; |
|
511 + default: |
|
512 + return (-1); |
|
513 + } |
|
514 + |
|
515 while (ssh_msg_recv(ctxt->pam_psock, &buffer) == 0) { |
|
516 type = buffer_get_char(&buffer); |
|
517 msg = buffer_get_string(&buffer, NULL); |
|
518 @@ -879,15 +806,6 @@ sshpam_query(void *ctx, char **name, cha |
|
519 /* FALLTHROUGH */ |
|
520 case PAM_AUTH_ERR: |
|
521 debug3("PAM: %s", pam_strerror(sshpam_handle, type)); |
|
522 - if (**prompts != NULL && strlen(**prompts) != 0) { |
|
523 - *info = **prompts; |
|
524 - **prompts = NULL; |
|
525 - *num = 0; |
|
526 - **echo_on = 0; |
|
527 - ctxt->pam_done = -1; |
|
528 - free(msg); |
|
529 - return 0; |
|
530 - } |
|
531 /* FALLTHROUGH */ |
|
532 case PAM_SUCCESS: |
|
533 if (**prompts != NULL) { |
|
534 @@ -898,25 +816,21 @@ sshpam_query(void *ctx, char **name, cha |
|
535 free(**prompts); |
|
536 **prompts = NULL; |
|
537 } |
|
538 - if (type == PAM_SUCCESS) { |
|
539 - if (!sshpam_authctxt->valid || |
|
540 - (sshpam_authctxt->pw->pw_uid == 0 && |
|
541 - options.permit_root_login != PERMIT_YES)) |
|
542 - fatal("Internal error: PAM auth " |
|
543 - "succeeded when it should have " |
|
544 - "failed"); |
|
545 - import_environments(&buffer); |
|
546 - *num = 0; |
|
547 - **echo_on = 0; |
|
548 - ctxt->pam_done = 1; |
|
549 - free(msg); |
|
550 - return (0); |
|
551 + /* send accumulated messages to parent */ |
|
552 + buffer_clear(&buffer); |
|
553 + buffer_put_cstring(&buffer, buffer_ptr(&loginmsg)); |
|
554 + if (!use_privsep) { |
|
555 + /* sync packet state with parrent */ |
|
556 + ssh = active_state; |
|
557 + r = ssh_packet_get_state(ssh, &buffer); |
|
558 + if (r != 0) |
|
559 + fatal("%s: get_state failed: %s", |
|
560 + __func__, ssh_err(r)); |
|
561 } |
|
562 - error("PAM: %s for %s%.100s from %.100s", msg, |
|
563 - sshpam_authctxt->valid ? "" : "illegal user ", |
|
564 - sshpam_authctxt->user, |
|
565 - get_remote_name_or_ip(utmp_len, options.use_dns)); |
|
566 - /* FALLTHROUGH */ |
|
567 + ssh_msg_send(ctxt->pam_psock, type, &buffer); |
|
568 + /* callback child ends here */ |
|
569 + close(ctxt->pam_psock); |
|
570 + exit(0); |
|
571 default: |
|
572 *num = 0; |
|
573 **echo_on = 0; |
|
574 @@ -970,7 +884,7 @@ sshpam_free_ctx(void *ctxtp) |
|
575 struct pam_ctxt *ctxt = ctxtp; |
|
576 |
|
577 debug3("PAM: %s entering", __func__); |
|
578 - sshpam_thread_cleanup(); |
|
579 + sshpam_child_cleanup(); |
|
580 free(ctxt); |
|
581 /* |
|
582 * We don't call sshpam_cleanup() here because we may need the PAM |
|
583 diff -pur old/auth-pam.h new/auth-pam.h |
|
584 --- old/auth-pam.h |
|
585 +++ new/auth-pam.h |
|
586 @@ -45,9 +45,10 @@ int do_pam_putenv(char *, char *); |
|
587 char ** fetch_pam_environment(void); |
|
588 char ** fetch_pam_child_environment(void); |
|
589 void free_pam_environment(char **); |
|
590 -void sshpam_thread_cleanup(void); |
|
591 +void sshpam_child_cleanup(void); |
|
592 void sshpam_cleanup(void); |
|
593 int sshpam_auth_passwd(Authctxt *, const char *); |
|
594 int is_pam_session_open(void); |
|
595 +int get_pam_done(void *); |
|
596 |
|
597 #endif /* USE_PAM */ |
|
598 diff -pur old/monitor.c new/monitor.c |
|
599 --- old/monitor.c |
|
600 +++ new/monitor.c |
|
601 @@ -1179,12 +1179,38 @@ mm_answer_pam_init_ctx(int sock, Buffer |
|
602 sshpam_ctxt = (sshpam_device.init_ctx)(authctxt); |
|
603 sshpam_authok = NULL; |
|
604 buffer_clear(m); |
|
605 + int pam_done = 0; |
|
606 if (sshpam_ctxt != NULL) { |
|
607 monitor_permit(mon_dispatch, MONITOR_REQ_PAM_FREE_CTX, 1); |
|
608 buffer_put_int(m, 1); |
|
609 } else { |
|
610 buffer_put_int(m, 0); |
|
611 } |
|
612 + |
|
613 + /* pam conversation successfully finished in child process */ |
|
614 + if (sshpam_ctxt != NULL && |
|
615 + (pam_done = get_pam_done(sshpam_ctxt)) != 0) { |
|
616 + auth_method = "keyboard-interactive"; |
|
617 + auth_submethod = "pam"; |
|
618 + /* |
|
619 + * ANS_PAM_INIT_CTX already sent by callback child. |
|
620 + * Privsep child now expects ANS_PAM_QUERY. |
|
621 + */ |
|
622 + buffer_clear(m); |
|
623 + buffer_put_int(m, 0); /* ret */ |
|
624 + buffer_put_cstring(m, ""); /* name */ |
|
625 + if (pam_done == 1) { /* info */ |
|
626 + buffer_put_cstring(m, ""); |
|
627 + } else { |
|
628 + buffer_put_string(m, buffer_ptr(&loginmsg), |
|
629 + buffer_len(&loginmsg)); |
|
630 + buffer_clear(&loginmsg); |
|
631 + } |
|
632 + buffer_put_int(m, 0); /* num */ |
|
633 + mm_request_send(sock, MONITOR_ANS_PAM_QUERY, m); |
|
634 + return (0); |
|
635 + } |
|
636 + |
|
637 mm_request_send(sock, MONITOR_ANS_PAM_INIT_CTX, m); |
|
638 return (0); |
|
639 } |
|
640 @@ -1938,7 +1964,8 @@ monitor_apply_keystate(struct monitor *p |
|
641 int r; |
|
642 |
|
643 debug3("%s: packet_set_state", __func__); |
|
644 - if ((r = ssh_packet_set_state(ssh, child_state)) != 0) |
|
645 + if ((r = ssh_packet_set_state(ssh, child_state)) != 0 || |
|
646 + (r = ssh_packet_set_postauth(ssh)) != 0) |
|
647 fatal("%s: packet_set_state: %s", __func__, ssh_err(r)); |
|
648 sshbuf_free(child_state); |
|
649 child_state = NULL; |
|
650 diff -pur old/packet.c new/packet.c |
|
651 --- old/packet.c |
|
652 +++ new/packet.c |
|
653 @@ -2345,7 +2345,7 @@ ssh_packet_restore_state(struct ssh *ssh |
|
654 } |
|
655 |
|
656 /* Reset after_authentication and reset compression in post-auth privsep */ |
|
657 -static int |
|
658 +int |
|
659 ssh_packet_set_postauth(struct ssh *ssh) |
|
660 { |
|
661 struct sshcomp *comp; |
|
662 @@ -2682,8 +2682,7 @@ ssh_packet_set_state(struct ssh *ssh, st |
|
663 cipher_set_keycontext(&state->send_context, keyout); |
|
664 cipher_set_keycontext(&state->receive_context, keyin); |
|
665 |
|
666 - if ((r = ssh_packet_set_compress_state(ssh, m)) != 0 || |
|
667 - (r = ssh_packet_set_postauth(ssh)) != 0) |
|
668 + if ((r = ssh_packet_set_compress_state(ssh, m)) != 0) |
|
669 return r; |
|
670 |
|
671 sshbuf_reset(state->input); |
|
672 diff -pur old/packet.h new/packet.h |
|
673 --- old/packet.h |
|
674 +++ new/packet.h |
|
675 @@ -141,6 +141,7 @@ u_int ssh_packet_get_maxsize(struct ssh |
|
676 |
|
677 int ssh_packet_get_state(struct ssh *, struct sshbuf *); |
|
678 int ssh_packet_set_state(struct ssh *, struct sshbuf *); |
|
679 +int ssh_packet_set_postauth(struct ssh *ssh); |
|
680 |
|
681 const char *ssh_remote_ipaddr(struct ssh *); |
|
682 |
|
683 diff -pur old/servconf.c new/servconf.c |
|
684 --- old/servconf.c |
|
685 +++ new/servconf.c |
|
686 @@ -433,6 +433,18 @@ fill_default_server_options(ServerOption |
|
687 options->compression = 0; |
|
688 } |
|
689 #endif |
|
690 +#ifdef USE_PAM |
|
691 + if (!use_privsep && options->compression == COMP_ZLIB && |
|
692 + options->use_pam && |
|
693 + (options->kbd_interactive_authentication || |
|
694 + options->challenge_response_authentication)) { |
|
695 + error("Compression algorithm 'zlib' is not supported for " |
|
696 + "PAM authentication when privilege separation is off"); |
|
697 + error("Limmiting compression algorithms to " |
|
698 + "'none,[email protected]'"); |
|
699 + options->compression = COMP_DELAYED; |
|
700 + } |
|
701 +#endif |
|
702 |
|
703 } |
|
704 |
|
705 diff -pur old/session.c new/session.c |
|
706 --- old/session.c |
|
707 +++ new/session.c |
|
708 @@ -2850,7 +2850,7 @@ do_cleanup(Authctxt *authctxt) |
|
709 #ifdef USE_PAM |
|
710 if (options.use_pam) { |
|
711 sshpam_cleanup(); |
|
712 - sshpam_thread_cleanup(); |
|
713 + sshpam_child_cleanup(); |
|
714 } |
|
715 #endif |
|
716 |