Package: libpam-ssh
Version: 1.91.0-6
Followup-For: Bug #294181

If at first it cannot connect to an agent, it removes the old
per-agent file and tries again.  If fails again (shouldn't happen)
gives up.

-- System Information:
Debian Release: 3.1
  APT prefers unstable
  APT policy: (500, 'unstable')
Architecture: i386 (i686)
Kernel: Linux 2.6.10-pisicuta-7
Locale: LANG=en_GB.UTF-8, LC_CTYPE=en_GB.UTF-8 (charmap=UTF-8)

Versions of packages libpam-ssh depends on:
ii  libc6                       2.3.2.ds1-20 GNU C Library: Shared libraries an
ii  libpam0g                    0.76-22      Pluggable Authentication Modules l
ii  libssl0.9.7                 0.9.7e-3     SSL shared libraries

-- no debconf information
--- libpam-ssh-1.91.0.orig/pam_ssh.c	2004-04-12 08:55:08.000000000 -0500
+++ libpam-ssh-1.91.0/pam_ssh.c	2005-04-03 21:18:58.140936716 -0500
@@ -279,9 +279,8 @@
  */
 
 static int
-add_keys(pam_handle_t *pamh, char *socket)
+add_keys(pam_handle_t *pamh, AuthenticationConnection *ac)
 {
-	AuthenticationConnection *ac;	/* connection to ssh-agent */
 	char *comment;			/* private key comment */
 	char *data_name;		/* PAM state */
 	int final;			/* final return value */
@@ -289,13 +288,6 @@
 	Key *key;			/* user's private key */
 	int retval;			/* from calls */
 
-	/* connect to the agent */
-
-	if (!(ac = ssh_get_authentication_connection(socket))) {
-		pam_ssh_log(LOG_ERR, "%s: %m", socket);
-		return PAM_SESSION_ERR;
-	}
-
 	/* hand off each private key to the agent */
 
 	final = 0;
@@ -324,11 +316,177 @@
 		if (!final)
 			final = retval;
 	}
-	ssh_close_authentication_connection(ac);
 
 	return final ? PAM_SUCCESS : PAM_SESSION_ERR;
 }
 
+static int
+start_ssh_agent(pam_handle_t *pamh, uid_t uid, FILE **env_read)
+{
+	pid_t child_pid;		/* child process that spawns agent */
+	int child_pipe[2];		/* pipe to child process */
+	int child_status;		/* child process status */
+	char *arg[3], *env[1];		/* to pass to execve() */
+
+	if (pipe(child_pipe) < 0) {
+		pam_ssh_log(LOG_ERR, "pipe: %m");
+		return PAM_SERVICE_ERR;
+	}
+	switch (child_pid = fork()) {
+	case -1:	/* error */
+		pam_ssh_log(LOG_ERR, "fork: %m");
+		close(child_pipe[0]);
+		close(child_pipe[1]);
+		return PAM_SERVICE_ERR;
+		/* NOTREACHED */
+	case 0:		/* child */
+
+		/* Permanently drop privileges using setuid()
+			 before executing ssh-agent so that root
+			 privileges can't possibly be regained (some
+			 ssh-agents insist that euid == ruid
+			 anyway).  System V won't let us use
+			 setuid() unless euid == 0, so we
+			 temporarily regain root privileges first
+			 with openpam_restore_cred() (which calls
+			 seteuid()). */
+
+		switch (openpam_restore_cred(pamh)) {
+		case PAM_SYSTEM_ERR:
+			pam_ssh_log(LOG_ERR,
+			            "can't restore privileges: %m");
+			_exit(EX_OSERR);
+			/* NOTREACHED */
+		case PAM_SUCCESS:
+			if (setuid(uid) == -1) {
+				pam_ssh_log(LOG_ERR,
+				            "can't drop privileges: %m",
+				            uid);
+				_exit(EX_NOPERM);
+			}
+			break;
+		}
+
+		if (close(child_pipe[0]) == -1) {
+			pam_ssh_log(LOG_ERR, "close: %m");
+			_exit(EX_OSERR);
+		}
+		if (child_pipe[1] != STDOUT_FILENO) {
+			if (dup2(child_pipe[1], STDOUT_FILENO) == -1) {
+				pam_ssh_log(LOG_ERR, "dup: %m");
+				_exit(EX_OSERR);
+			}
+			if (close(child_pipe[1]) == -1) {
+				pam_ssh_log(LOG_ERR, "close: %m");
+				_exit(EX_OSERR);
+			}
+		}
+		arg[0] = "ssh-agent";
+		arg[1] = "-s";
+		arg[2] = NULL;
+		env[0] = NULL;
+		execve(PATH_SSH_AGENT, arg, env);
+		pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
+		_exit(127);
+		/* NOTREACHED */
+	}
+	if (close(child_pipe[1]) == -1) {
+		pam_ssh_log(LOG_ERR, "close: %m");
+		return PAM_SESSION_ERR;
+	}
+	if (!(*env_read = fdopen(child_pipe[0], "r"))) {
+		pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
+		return PAM_SESSION_ERR;
+	}
+
+	child_status = 0;
+	if (waitpid_intr(child_pid, &child_status, 0) == -1 &&
+			errno != ECHILD) {
+		pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
+		return PAM_SESSION_ERR;
+	}
+
+	if (child_status != 0) {
+		if (WIFSIGNALED(child_status))
+			pam_ssh_log(LOG_ERR, "%s exited on signal %d",
+									PATH_SSH_AGENT, WTERMSIG(child_status));
+		else
+			if (WEXITSTATUS(child_status) == 127)
+				pam_ssh_log(LOG_ERR,
+				            "cannot execute %s",
+				            PATH_SSH_AGENT);
+			else
+				pam_ssh_log(LOG_ERR,
+				            "%s exited with status %d",
+				            PATH_SSH_AGENT,
+				            WEXITSTATUS(child_status));
+		return PAM_SESSION_ERR;
+	}
+
+	return PAM_SUCCESS;
+}
+
+static int
+read_write_agent_env(pam_handle_t *pamh,
+                     FILE *env_read,
+                     int env_write,
+                     char **agent_socket)
+{
+	char *agent_pid;		/* copy of agent PID */
+	char *env_end;			/* end of env */
+	char env_string[BUFSIZ];	/* environment string */
+	char *env_value;		/* envariable value */
+	int retval;			/* from calls */
+
+	while (fgets(env_string, sizeof env_string, env_read)) {
+
+		/* parse environment definitions */
+
+		if (env_write >= 0)
+			write(env_write, env_string, strlen(env_string));
+		if (!(env_value = strchr(env_string, '=')) ||
+		    !(env_end = strchr(env_value, ';')))
+			continue;
+		*env_end = '\0';
+
+		/* pass to the application */
+
+		if ((retval = pam_putenv(pamh, env_string)) != PAM_SUCCESS)
+			return retval;
+
+		*env_value++ = '\0';
+
+		/* save the agent socket so we can connect to it and add
+                   the keys as well as the PID so we can kill the agent on
+                   session close. */
+
+		agent_pid = NULL;
+		if (strcmp(&env_string[strlen(env_string) -
+		    strlen(ENV_SOCKET_SUFFIX)], ENV_SOCKET_SUFFIX) == 0 &&
+		    !(*agent_socket = strdup(env_value))) {
+			pam_ssh_log(LOG_CRIT, "out of memory");
+			return PAM_SERVICE_ERR;
+		} else if (strcmp(&env_string[strlen(env_string) -
+		    strlen(ENV_PID_SUFFIX)], ENV_PID_SUFFIX) == 0 &&
+		    (!(agent_pid = strdup(env_value)) ||
+		    (retval = pam_set_data(pamh, "ssh_agent_pid",
+		    agent_pid, ssh_cleanup)) != PAM_SUCCESS)) {
+			if (agent_pid)
+				free(agent_pid);
+			else {
+				pam_ssh_log(LOG_CRIT, "out of memory");
+				return PAM_SERVICE_ERR;
+			}
+			if (agent_socket)
+				free(agent_socket);
+			return retval;
+		}
+
+	}
+
+	return PAM_SUCCESS;
+}
+
 
 PAM_EXTERN int
 pam_sm_authenticate(pam_handle_t *pamh, int flags __unused, int argc,
@@ -494,17 +652,10 @@
 pam_sm_open_session(pam_handle_t *pamh, int flags __unused,
     int argc __unused, const char **argv __unused)
 {
-	char *agent_pid;		/* copy of agent PID */
+	AuthenticationConnection *ac;	/* connection to ssh-agent */
 	char *agent_socket;		/* agent socket */
-	char *arg[3], *env[1];		/* to pass to execve() */
-	pid_t child_pid;		/* child process that spawns agent */
-	int child_pipe[2];		/* pipe to child process */
-	int child_status;		/* child process status */
 	char *cp;			/* scratch */
-	char *env_end;			/* end of env */
 	FILE *env_read;			/* env data source */
-	char env_string[BUFSIZ];	/* environment string */
-	char *env_value;		/* envariable value */
 	int env_write;			/* env file descriptor */
 	char hname[MAXHOSTNAMELEN];	/* local hostname */
 	int no_link;			/* link per-agent file? */
@@ -515,6 +666,7 @@
 	int start_agent;		/* start agent? */
 	const char *tty_raw;		/* raw tty or display name */
 	char *tty_nodir;		/* tty without / chars */
+	int attempt;			/* No. of attempt to contact agent */
 
 	log_init(MODULE_NAME, SYSLOG_LEVEL_ERROR, SYSLOG_FACILITY_AUTHPRIV, 0);
 
@@ -568,215 +720,70 @@
            per-session filename later.  Start the agent if we can't open
 	   the file for reading. */
 
-	env_write = child_pid = no_link = start_agent = 0;
-	env_read = NULL;
-	if ((env_write = open(per_agent, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR))
-	    < 0 && !(env_read = fopen(per_agent, "r")))
-		no_link = 1;
-	if (!env_read) {
-		start_agent = 1;
-		if (pipe(child_pipe) < 0) {
-			pam_ssh_log(LOG_ERR, "pipe: %m");
-			close(env_write);
-			openpam_restore_cred(pamh);
-			return PAM_SERVICE_ERR;
-		}
-		switch (child_pid = fork()) {
-		case -1:	/* error */
-			pam_ssh_log(LOG_ERR, "fork: %m");
-			close(child_pipe[0]);
-			close(child_pipe[1]);
-			close(env_write);
-			openpam_restore_cred(pamh);
-			return PAM_SERVICE_ERR;
-			/* NOTREACHED */
-		case 0:		/* child */
-
-			/* Permanently drop privileges using setuid()
-			   before executing ssh-agent so that root
-			   privileges can't possibly be regained (some
-			   ssh-agents insist that euid == ruid
-			   anyway).  System V won't let us use
-			   setuid() unless euid == 0, so we
-			   temporarily regain root privileges first
-			   with openpam_restore_cred() (which calls
-			   seteuid()). */
-
-			switch (openpam_restore_cred(pamh)) {
-			case PAM_SYSTEM_ERR:
-				pam_ssh_log(LOG_ERR,
-				    "can't restore privileges: %m");
-				_exit(EX_OSERR);
-				/* NOTREACHED */
-			case PAM_SUCCESS:
-				if (setuid(pwent->pw_uid) == -1) {
-					pam_ssh_log(LOG_ERR,
-					    "can't drop privileges: %m",
-					    pwent->pw_uid);
-					_exit(EX_NOPERM);
-				}
-				break;
-			}
-
-			if (close(child_pipe[0]) == -1) {
-				pam_ssh_log(LOG_ERR, "close: %m");
-				_exit(EX_OSERR);
-			}
-			if (child_pipe[1] != STDOUT_FILENO) {
-				if (dup2(child_pipe[1], STDOUT_FILENO) == -1) {
-					pam_ssh_log(LOG_ERR, "dup: %m");
-					_exit(EX_OSERR);
-				}
-				if (close(child_pipe[1]) == -1) {
-					pam_ssh_log(LOG_ERR, "close: %m");
-					_exit(EX_OSERR);
-				}
+	for ( attempt = 0; attempt < 2; ++attempt ) {
+		env_write = no_link = start_agent = 0;
+		env_read = NULL;
+		if ((env_write = open(per_agent, O_CREAT | O_EXCL | O_WRONLY, S_IRUSR))
+				< 0 && !(env_read = fopen(per_agent, "r")))
+			no_link = 1;
+		if (!env_read) {
+			start_agent = 1;
+			if ((retval = start_ssh_agent(pamh, pwent->pw_uid, &env_read))
+					!= PAM_SUCCESS) {
+				close(env_write);
+				openpam_restore_cred(pamh);
+				return retval;
 			}
-			arg[0] = "ssh-agent";
-			arg[1] = "-s";
-			arg[2] = NULL;
-			env[0] = NULL;
-			execve(PATH_SSH_AGENT, arg, env);
-			pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
-			_exit(127);
-			/* NOTREACHED */
-		}
-		if (close(child_pipe[1]) == -1) {
-			pam_ssh_log(LOG_ERR, "close: %m");
-			openpam_restore_cred(pamh);
-			return PAM_SESSION_ERR;
-		}
-		if (!(env_read = fdopen(child_pipe[0], "r"))) {
-			pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
-			close(env_write);
-			openpam_restore_cred(pamh);
-			return PAM_SESSION_ERR;
-		}
-	}
-
-	/* save environment for application with pam_putenv() */
-
-	agent_socket = NULL;
-	while (fgets(env_string, sizeof env_string, env_read)) {
-
-		/* parse environment definitions */
-
-		if (env_write >= 0)
-			write(env_write, env_string, strlen(env_string));
-		if (!(env_value = strchr(env_string, '=')) ||
-		    !(env_end = strchr(env_value, ';')))
-			continue;
-		*env_end = '\0';
-
-		/* pass to the application */
-
-		if ((retval = pam_putenv(pamh, env_string)) != PAM_SUCCESS) {
-			fclose(env_read);
-			if (start_agent)
-				waitpid_intr(child_pid, &child_status, 0);
-			close(env_write);
-			if (agent_socket)
-				free(agent_socket);
-			openpam_restore_cred(pamh);
-			return retval;
 		}
 
-		*env_value++ = '\0';
-
-		/* save the agent socket so we can connect to it and add
-                   the keys as well as the PID so we can kill the agent on
-                   session close. */
-
-		agent_pid = NULL;
-		if (strcmp(&env_string[strlen(env_string) -
-		    strlen(ENV_SOCKET_SUFFIX)], ENV_SOCKET_SUFFIX) == 0 &&
-		    !(agent_socket = strdup(env_value))) {
-			pam_ssh_log(LOG_CRIT, "out of memory");
-			fclose(env_read);
-			if (start_agent)
-				waitpid_intr(child_pid, &child_status, 0);
-			close(env_write);
+		agent_socket = NULL;
+		retval = read_write_agent_env(pamh, env_read, env_write, &agent_socket);
+		close(env_write);
+		if (retval != PAM_SUCCESS) {
 			if (agent_socket)
 				free(agent_socket);
-			openpam_restore_cred(pamh);
-			return PAM_SERVICE_ERR;
-		} else if (strcmp(&env_string[strlen(env_string) -
-		    strlen(ENV_PID_SUFFIX)], ENV_PID_SUFFIX) == 0 &&
-		    (!(agent_pid = strdup(env_value)) ||
-		    (retval = pam_set_data(pamh, "ssh_agent_pid",
-		    agent_pid, ssh_cleanup)) != PAM_SUCCESS)) {
 			fclose(env_read);
-			if (start_agent)
-				waitpid_intr(child_pid, &child_status, 0);
-			close(env_write);
-			if (agent_pid)
-				free(agent_pid);
-			else {
-				pam_ssh_log(LOG_CRIT, "out of memory");
-				openpam_restore_cred(pamh);
-				return PAM_SERVICE_ERR;
-			}
-			if (agent_socket)
-				free(agent_socket);
 			openpam_restore_cred(pamh);
 			return retval;
 		}
 
-	}
-	close(env_write);
-
-	if (fclose(env_read) != 0) {
-		pam_ssh_log(LOG_ERR, "fclose: %m");
-		openpam_restore_cred(pamh);
-		return PAM_SESSION_ERR;
-	}
-
-	if (start_agent) {
-
-		/* Ignore ECHILD in case a SIGCHLD handler is installed. */
-
-		child_status = 0;
-		if (waitpid_intr(child_pid, &child_status, 0) == -1 &&
-		    errno != ECHILD) {
-			pam_ssh_log(LOG_ERR, "%s: %m", PATH_SSH_AGENT);
+		if (fclose(env_read) != 0) {
+			pam_ssh_log(LOG_ERR, "fclose: %m");
 			if (agent_socket)
 				free(agent_socket);
 			openpam_restore_cred(pamh);
 			return PAM_SESSION_ERR;
 		}
 
-		if (child_status != 0) {
-			if (WIFSIGNALED(child_status))
-				pam_ssh_log(LOG_ERR, "%s exited on signal %d",
-				    PATH_SSH_AGENT, WTERMSIG(child_status));
-			else
-				if (WEXITSTATUS(retval) == 127)
-					pam_ssh_log(LOG_ERR,
-					    "cannot execute %s",
-					    PATH_SSH_AGENT);
-				else
-					pam_ssh_log(LOG_ERR,
-					    "%s exited with status %d",
-					    PATH_SSH_AGENT,
-					    WEXITSTATUS(child_status));
-			if (agent_socket)
-				free(agent_socket);
+		if (!agent_socket) {
 			openpam_restore_cred(pamh);
 			return PAM_SESSION_ERR;
 		}
+
+		ac = ssh_get_authentication_connection(agent_socket);
+		if (ac) {
+			free(agent_socket);
+			break;
+		}
+		pam_ssh_log(LOG_ERR, "%s: %m", agent_socket);
+		free(agent_socket);
+		if (start_agent)
+			break;
+		unlink(per_agent);
 	}
 
-	if (!agent_socket) {
-		openpam_restore_cred(pamh);
+	if (!ac)
 		return PAM_SESSION_ERR;
-	}
 
-	if (start_agent && (retval = add_keys(pamh, agent_socket))
-	    != PAM_SUCCESS) {
+	if (start_agent)
+		retval = add_keys(pamh, ac);
+
+	ssh_close_authentication_connection(ac);
+
+	if (start_agent && retval != PAM_SUCCESS) {
 		openpam_restore_cred(pamh);
 		return retval;
 	}
-	free(agent_socket);
 
 	/* if we couldn't access the per-agent file, don't link a
            per-session filename to it */

Reply via email to