components/openssh/patches/041-pam_ctx_preserve.patch
changeset 5612 ece68a956e2f
child 6930 31ef2580c45d
equal deleted inserted replaced
5610:3fd0658e8699 5612:ece68a956e2f
       
     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