xref: /titanic_50/usr/src/cmd/ssh/sshd/altprivsep.c (revision dbe3f931a78f2d36a72003f6b51bf1fdc3aa035e)
17c478bd9Sstevel@tonic-gate /*
27c478bd9Sstevel@tonic-gate  * CDDL HEADER START
37c478bd9Sstevel@tonic-gate  *
47c478bd9Sstevel@tonic-gate  * The contents of this file are subject to the terms of the
59a8058b5Sjp161948  * Common Development and Distribution License (the "License").
69a8058b5Sjp161948  * You may not use this file except in compliance with the License.
77c478bd9Sstevel@tonic-gate  *
87c478bd9Sstevel@tonic-gate  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
97c478bd9Sstevel@tonic-gate  * or http://www.opensolaris.org/os/licensing.
107c478bd9Sstevel@tonic-gate  * See the License for the specific language governing permissions
117c478bd9Sstevel@tonic-gate  * and limitations under the License.
127c478bd9Sstevel@tonic-gate  *
137c478bd9Sstevel@tonic-gate  * When distributing Covered Code, include this CDDL HEADER in each
147c478bd9Sstevel@tonic-gate  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
157c478bd9Sstevel@tonic-gate  * If applicable, add the following below this CDDL HEADER, with the
167c478bd9Sstevel@tonic-gate  * fields enclosed by brackets "[]" replaced with your own identifying
177c478bd9Sstevel@tonic-gate  * information: Portions Copyright [yyyy] [name of copyright owner]
187c478bd9Sstevel@tonic-gate  *
197c478bd9Sstevel@tonic-gate  * CDDL HEADER END
207c478bd9Sstevel@tonic-gate  *
21*dbe3f931Sjp161948  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
227c478bd9Sstevel@tonic-gate  * Use is subject to license terms.
237c478bd9Sstevel@tonic-gate  */
247c478bd9Sstevel@tonic-gate 
257c478bd9Sstevel@tonic-gate #pragma ident	"%Z%%M%	%I%	%E% SMI"
267c478bd9Sstevel@tonic-gate 
277c478bd9Sstevel@tonic-gate #include "includes.h"
287c478bd9Sstevel@tonic-gate #include "atomicio.h"
297c478bd9Sstevel@tonic-gate #include "auth.h"
307c478bd9Sstevel@tonic-gate #include "bufaux.h"
317c478bd9Sstevel@tonic-gate #include "buffer.h"
327c478bd9Sstevel@tonic-gate #include "cipher.h"
337c478bd9Sstevel@tonic-gate #include "compat.h"
347c478bd9Sstevel@tonic-gate #include "dispatch.h"
357c478bd9Sstevel@tonic-gate #include "getput.h"
367c478bd9Sstevel@tonic-gate #include "kex.h"
377c478bd9Sstevel@tonic-gate #include "log.h"
387c478bd9Sstevel@tonic-gate #include "mac.h"
397c478bd9Sstevel@tonic-gate #include "packet.h"
407c478bd9Sstevel@tonic-gate #include "uidswap.h"
417c478bd9Sstevel@tonic-gate #include "ssh2.h"
427c478bd9Sstevel@tonic-gate #include "sshlogin.h"
437c478bd9Sstevel@tonic-gate #include "xmalloc.h"
447c478bd9Sstevel@tonic-gate #include "altprivsep.h"
457c478bd9Sstevel@tonic-gate #include <fcntl.h>
467c478bd9Sstevel@tonic-gate #include <sys/types.h>
477c478bd9Sstevel@tonic-gate #include <sys/types.h>
487c478bd9Sstevel@tonic-gate #include <sys/stat.h>
497c478bd9Sstevel@tonic-gate #include <sys/socket.h>
507c478bd9Sstevel@tonic-gate 
517c478bd9Sstevel@tonic-gate extern Kex *xxx_kex;
527c478bd9Sstevel@tonic-gate 
537c478bd9Sstevel@tonic-gate static Buffer to_monitor;
547c478bd9Sstevel@tonic-gate static Buffer from_monitor;
557c478bd9Sstevel@tonic-gate 
567c478bd9Sstevel@tonic-gate /*
577c478bd9Sstevel@tonic-gate  * Sun's Alternative Privilege Separation basics:
587c478bd9Sstevel@tonic-gate  *
597c478bd9Sstevel@tonic-gate  * Abstract
607c478bd9Sstevel@tonic-gate  * --------
617c478bd9Sstevel@tonic-gate  *
627c478bd9Sstevel@tonic-gate  * sshd(1M) fork()s and drops privs in the child while retaining privs
637c478bd9Sstevel@tonic-gate  * in the parent (a.k.a., the monitor).  The unprivileged sshd and the
647c478bd9Sstevel@tonic-gate  * monitor talk over a pipe using a simple protocol.
657c478bd9Sstevel@tonic-gate  *
667c478bd9Sstevel@tonic-gate  * The monitor protocol is all about having the monitor carry out the
677c478bd9Sstevel@tonic-gate  * only operations that require privileges OR access to privileged
687c478bd9Sstevel@tonic-gate  * resources.  These are: utmpx/wtmpx record keeping, auditing, and
697c478bd9Sstevel@tonic-gate  * SSHv2 re-keying.
707c478bd9Sstevel@tonic-gate  *
717c478bd9Sstevel@tonic-gate  * Re-Keying
727c478bd9Sstevel@tonic-gate  * ---------
737c478bd9Sstevel@tonic-gate  *
747c478bd9Sstevel@tonic-gate  * Re-keying is the only protocol version specific aspect of sshd in
757c478bd9Sstevel@tonic-gate  * which the monitor gets involved.
767c478bd9Sstevel@tonic-gate  *
777c478bd9Sstevel@tonic-gate  * The monitor processes all SSHv2 re-key protocol packets, but the
787c478bd9Sstevel@tonic-gate  * unprivileged sshd process does the transport layer crypto for those
797c478bd9Sstevel@tonic-gate  * packets.
807c478bd9Sstevel@tonic-gate  *
817c478bd9Sstevel@tonic-gate  * The monitor and its unprivileged sshd child process treat
827c478bd9Sstevel@tonic-gate  * SSH_MSG_NEWKEYS SSH2 messages specially: a) the monitor does not call
837c478bd9Sstevel@tonic-gate  * set_newkeys(), but b) the child asks the monitor for the set of
847c478bd9Sstevel@tonic-gate  * negotiated algorithms, key, IV and what not for the relevant
857c478bd9Sstevel@tonic-gate  * transport direction and then calls set_newkeys().
867c478bd9Sstevel@tonic-gate  *
877c478bd9Sstevel@tonic-gate  * Monitor Protocol
887c478bd9Sstevel@tonic-gate  * ----------------
897c478bd9Sstevel@tonic-gate  *
907c478bd9Sstevel@tonic-gate  * Monitor IPC message formats are similar to SSHv2 messages, minus
917c478bd9Sstevel@tonic-gate  * compression, encryption, padding and MACs:
927c478bd9Sstevel@tonic-gate  *
937c478bd9Sstevel@tonic-gate  *  - 4 octet message length
947c478bd9Sstevel@tonic-gate  *  - message data
957c478bd9Sstevel@tonic-gate  *     - 1 octet message type
967c478bd9Sstevel@tonic-gate  *     - message data
977c478bd9Sstevel@tonic-gate  *
987c478bd9Sstevel@tonic-gate  * In broad strokes:
997c478bd9Sstevel@tonic-gate  *
1007c478bd9Sstevel@tonic-gate  *  - IPC: pipe, exit(2)/wait4(2)
1017c478bd9Sstevel@tonic-gate  *
1027c478bd9Sstevel@tonic-gate  *  - threads: the monitor and child are single-threaded
1037c478bd9Sstevel@tonic-gate  *
1047c478bd9Sstevel@tonic-gate  *  - monitor main loop: a variant of server_loop2(), for re-keying only
1057c478bd9Sstevel@tonic-gate  *  - unpriv child main loop: server_loop2(), as usual
1067c478bd9Sstevel@tonic-gate  *
1077c478bd9Sstevel@tonic-gate  *  - protocol:
1087c478bd9Sstevel@tonic-gate  *     - key exchange packets are always forwarded as is to the monitor
1097c478bd9Sstevel@tonic-gate  *     - newkeys, record_login(), record_logout() are special packets
1107c478bd9Sstevel@tonic-gate  *     using the packet type range reserved for local extensions
1117c478bd9Sstevel@tonic-gate  *
1127c478bd9Sstevel@tonic-gate  *  - the child drops privs and runs like a normal sshd, except that it
1137c478bd9Sstevel@tonic-gate  *  sets dispatch handlers for key exchange packets that forward the
1147c478bd9Sstevel@tonic-gate  *  packets to the monitor
1157c478bd9Sstevel@tonic-gate  *
1167c478bd9Sstevel@tonic-gate  * Event loops:
1177c478bd9Sstevel@tonic-gate  *
1187c478bd9Sstevel@tonic-gate  *  - all monitor protocols are synchronous: because the SSHv2 rekey
1197c478bd9Sstevel@tonic-gate  *  protocols are synchronous and because the other monitor operations
1207c478bd9Sstevel@tonic-gate  *  are synchronous (or have no replies),
1217c478bd9Sstevel@tonic-gate  *
1227c478bd9Sstevel@tonic-gate  *  - server_loop2() is modified to check the monitor pipe for rekey
1237c478bd9Sstevel@tonic-gate  *  packets to forward to the client
1247c478bd9Sstevel@tonic-gate  *
1257c478bd9Sstevel@tonic-gate  *  - and dispatch handlers are set, upon receipt of KEXINIT (and reset
1267c478bd9Sstevel@tonic-gate  *  when NEWKEYS is sent out) to forward incoming rekey packets to the
1277c478bd9Sstevel@tonic-gate  *  monitor.
1287c478bd9Sstevel@tonic-gate  *
1297c478bd9Sstevel@tonic-gate  *  - the monitor runs an event loop not unlike server_loop2() and runs
1307c478bd9Sstevel@tonic-gate  *  key exchanges almost exactly as a pre-altprivsep sshd would
1317c478bd9Sstevel@tonic-gate  *
1327c478bd9Sstevel@tonic-gate  *  - unpriv sshd exit -> monitor cleanup (including audit logout) and exit
1337c478bd9Sstevel@tonic-gate  *
1347c478bd9Sstevel@tonic-gate  *  - fatal() in monitor -> forcibly shutdown() socket and kill/wait for
1357c478bd9Sstevel@tonic-gate  *  child (so that the audit event for the logout better reflects
1367c478bd9Sstevel@tonic-gate  *  reality -- i.e., logged out means logged out, but for bg jobs)
1377c478bd9Sstevel@tonic-gate  *
1387c478bd9Sstevel@tonic-gate  * Message formats:
1397c478bd9Sstevel@tonic-gate  *
1407c478bd9Sstevel@tonic-gate  *  - key exchange packets/replies forwarded "as is"
1417c478bd9Sstevel@tonic-gate  *
1427c478bd9Sstevel@tonic-gate  *  - all other monitor requests are sent as SSH2_PRIV_MSG_ALTPRIVSEP and have a
1437c478bd9Sstevel@tonic-gate  *  sub-type identifier (one octet)
1447c478bd9Sstevel@tonic-gate  *  - private request sub-types include:
1457c478bd9Sstevel@tonic-gate  *     - get new shared secret from last re-key
1467c478bd9Sstevel@tonic-gate  *     - record login  (utmpx/wtmpx), request data contains three arguments:
1477c478bd9Sstevel@tonic-gate  *     pid, ttyname, program name
1487c478bd9Sstevel@tonic-gate  *     - record logout (utmpx/wtmpx), request data contains one argument: pid
1497c478bd9Sstevel@tonic-gate  *
1507c478bd9Sstevel@tonic-gate  * Reply sub-types include:
1517c478bd9Sstevel@tonic-gate  *
1527c478bd9Sstevel@tonic-gate  *  - NOP (for record_login/logout)
1537c478bd9Sstevel@tonic-gate  *  - new shared secret from last re-key
1547c478bd9Sstevel@tonic-gate  */
1557c478bd9Sstevel@tonic-gate 
1567c478bd9Sstevel@tonic-gate static int aps_started = 0;
1577c478bd9Sstevel@tonic-gate static int is_monitor = 0;
1587c478bd9Sstevel@tonic-gate 
1597c478bd9Sstevel@tonic-gate static pid_t monitor_pid, child_pid;
1607c478bd9Sstevel@tonic-gate static int pipe_fds[2];
1617c478bd9Sstevel@tonic-gate static int pipe_fd = -1;
1627c478bd9Sstevel@tonic-gate static Buffer input_pipe, output_pipe; /* for pipe I/O */
1637c478bd9Sstevel@tonic-gate 
1647c478bd9Sstevel@tonic-gate static Authctxt *xxx_authctxt;
1657c478bd9Sstevel@tonic-gate 
1667c478bd9Sstevel@tonic-gate /* Monitor functions */
1677c478bd9Sstevel@tonic-gate extern void aps_monitor_loop(Authctxt *authctxt, int pipe, pid_t child_pid);
1687c478bd9Sstevel@tonic-gate static void aps_record_login(void);
1697c478bd9Sstevel@tonic-gate static void aps_record_logout(void);
170*dbe3f931Sjp161948 static void aps_start_rekex(void);
1717c478bd9Sstevel@tonic-gate 
1727c478bd9Sstevel@tonic-gate /* Altprivsep packet utilities for communication with the monitor */
1737c478bd9Sstevel@tonic-gate static void	altprivsep_packet_start(u_char);
1747c478bd9Sstevel@tonic-gate static int	altprivsep_packet_send(void);
1757c478bd9Sstevel@tonic-gate static int	altprivsep_fwd_packet(u_char type);
1767c478bd9Sstevel@tonic-gate 
1777c478bd9Sstevel@tonic-gate static int	altprivsep_packet_read(void);
1787c478bd9Sstevel@tonic-gate static void	altprivsep_packet_read_expect(int type);
1797c478bd9Sstevel@tonic-gate 
1807c478bd9Sstevel@tonic-gate static void	altprivsep_packet_put_char(int ch);
1817c478bd9Sstevel@tonic-gate static void	altprivsep_packet_put_int(u_int value);
1827c478bd9Sstevel@tonic-gate static void	altprivsep_packet_put_cstring(const char *str);
1837c478bd9Sstevel@tonic-gate static void	altprivsep_packet_put_raw(const void *buf, u_int len);
1847c478bd9Sstevel@tonic-gate 
1857c478bd9Sstevel@tonic-gate static u_int	 altprivsep_packet_get_char(void);
1867c478bd9Sstevel@tonic-gate static void	*altprivsep_packet_get_raw(u_int *length_ptr);
1877c478bd9Sstevel@tonic-gate static void	*altprivsep_packet_get_string(u_int *length_ptr);
1887c478bd9Sstevel@tonic-gate 
1897c478bd9Sstevel@tonic-gate /*
1907c478bd9Sstevel@tonic-gate  * Start monitor from privileged sshd process.
1917c478bd9Sstevel@tonic-gate  *
1927c478bd9Sstevel@tonic-gate  * Return values are like fork(2); the parent is the monitor.  The caller should
1937c478bd9Sstevel@tonic-gate  * fatal() on error.
1947c478bd9Sstevel@tonic-gate  *
1957c478bd9Sstevel@tonic-gate  * Privileges are dropped, on the unprivileged side, upon success.
1967c478bd9Sstevel@tonic-gate  */
1977c478bd9Sstevel@tonic-gate pid_t
1987c478bd9Sstevel@tonic-gate altprivsep_start_monitor(Authctxt *authctxt)
1997c478bd9Sstevel@tonic-gate {
2007c478bd9Sstevel@tonic-gate 	pid_t pid;
2017c478bd9Sstevel@tonic-gate 	int junk;
2027c478bd9Sstevel@tonic-gate 
2037c478bd9Sstevel@tonic-gate 	if (aps_started || authctxt == NULL || authctxt->pw == NULL)
2047c478bd9Sstevel@tonic-gate 		fatal("Monitor startup failed: missing state");
2057c478bd9Sstevel@tonic-gate 
2067c478bd9Sstevel@tonic-gate 	xxx_authctxt = authctxt;
2077c478bd9Sstevel@tonic-gate 
2087c478bd9Sstevel@tonic-gate 	packet_set_server();
2097c478bd9Sstevel@tonic-gate 
2107c478bd9Sstevel@tonic-gate 	buffer_init(&output_pipe);
2117c478bd9Sstevel@tonic-gate 	buffer_init(&input_pipe);
2127c478bd9Sstevel@tonic-gate 
2137c478bd9Sstevel@tonic-gate 	if (pipe(pipe_fds) != 0) {
2147c478bd9Sstevel@tonic-gate 		error("Monitor startup failure: could not create pipes: %s",
2157c478bd9Sstevel@tonic-gate 			strerror(errno));
2167c478bd9Sstevel@tonic-gate 		return (-1);
2177c478bd9Sstevel@tonic-gate 	}
2187c478bd9Sstevel@tonic-gate 
2197c478bd9Sstevel@tonic-gate 	(void) fcntl(pipe_fds[0], F_SETFD, FD_CLOEXEC);
2207c478bd9Sstevel@tonic-gate 	(void) fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC);
2217c478bd9Sstevel@tonic-gate 
2227c478bd9Sstevel@tonic-gate 	monitor_pid = getpid();
2237c478bd9Sstevel@tonic-gate 
2247c478bd9Sstevel@tonic-gate 	if ((pid = fork()) > 0) {
2257c478bd9Sstevel@tonic-gate 		/* parent */
2267c478bd9Sstevel@tonic-gate 		child_pid = pid;
2277c478bd9Sstevel@tonic-gate 
2287c478bd9Sstevel@tonic-gate 		debug2("Monitor pid %ld, unprivileged child pid %ld",
2297c478bd9Sstevel@tonic-gate 			monitor_pid, child_pid);
2307c478bd9Sstevel@tonic-gate 
2317c478bd9Sstevel@tonic-gate 		(void) close(pipe_fds[1]);
2327c478bd9Sstevel@tonic-gate 
2337c478bd9Sstevel@tonic-gate 		pipe_fd = pipe_fds[0];
2347c478bd9Sstevel@tonic-gate 
2357c478bd9Sstevel@tonic-gate 		if (fcntl(pipe_fd, F_SETFL, O_NONBLOCK) < 0)
2367c478bd9Sstevel@tonic-gate 			error("fcntl O_NONBLOCK: %.100s", strerror(errno));
2377c478bd9Sstevel@tonic-gate 
2389a8058b5Sjp161948 		/* signal readiness of monitor */
2397c478bd9Sstevel@tonic-gate 		(void) write(pipe_fd, &pid, sizeof (pid));
2407c478bd9Sstevel@tonic-gate 
2417c478bd9Sstevel@tonic-gate 		aps_started = 1;
2427c478bd9Sstevel@tonic-gate 		is_monitor = 1;
2437c478bd9Sstevel@tonic-gate 
2447c478bd9Sstevel@tonic-gate 		debug2("Monitor started");
2457c478bd9Sstevel@tonic-gate 
2467c478bd9Sstevel@tonic-gate 		set_log_txt_prefix("monitor ");
2477c478bd9Sstevel@tonic-gate 
2487c478bd9Sstevel@tonic-gate 		return (pid);
2497c478bd9Sstevel@tonic-gate 
2507c478bd9Sstevel@tonic-gate 	}
2517c478bd9Sstevel@tonic-gate 
2527c478bd9Sstevel@tonic-gate 	if (pid < 0) {
2537c478bd9Sstevel@tonic-gate 		debug2("Monitor startup failure: could not fork unprivileged"
2547c478bd9Sstevel@tonic-gate 			" process:  %s", strerror(errno));
2557c478bd9Sstevel@tonic-gate 		return (pid);
2567c478bd9Sstevel@tonic-gate 	}
2577c478bd9Sstevel@tonic-gate 
2587c478bd9Sstevel@tonic-gate 	/* caller should drop privs */
2597c478bd9Sstevel@tonic-gate 
2607c478bd9Sstevel@tonic-gate 	(void) close(pipe_fds[0]);
2617c478bd9Sstevel@tonic-gate 
2627c478bd9Sstevel@tonic-gate 	pipe_fd = pipe_fds[1];
2637c478bd9Sstevel@tonic-gate 
2647c478bd9Sstevel@tonic-gate 	/* wait for monitor to be ready */
2657c478bd9Sstevel@tonic-gate 	debug2("Waiting for monitor");
2667c478bd9Sstevel@tonic-gate 	(void) read(pipe_fd, &junk, sizeof (junk));
2677c478bd9Sstevel@tonic-gate 	debug2("Monitor signalled readiness");
2687c478bd9Sstevel@tonic-gate 
2697c478bd9Sstevel@tonic-gate 	if (fcntl(pipe_fd, F_SETFL, O_NONBLOCK) < 0)
2707c478bd9Sstevel@tonic-gate 		error("fcntl O_NONBLOCK: %.100s", strerror(errno));
2717c478bd9Sstevel@tonic-gate 
2727c478bd9Sstevel@tonic-gate 	buffer_init(&to_monitor);
2737c478bd9Sstevel@tonic-gate 	buffer_init(&from_monitor);
2747c478bd9Sstevel@tonic-gate 
2757c478bd9Sstevel@tonic-gate 	if (compat20) {
2767c478bd9Sstevel@tonic-gate 		debug3("Setting handler to forward re-key packets to monitor");
2777c478bd9Sstevel@tonic-gate 		dispatch_range(SSH2_MSG_KEXINIT, SSH2_MSG_TRANSPORT_MAX,
2787c478bd9Sstevel@tonic-gate 			&altprivsep_rekey);
2797c478bd9Sstevel@tonic-gate 	}
2807c478bd9Sstevel@tonic-gate 
2817c478bd9Sstevel@tonic-gate 	/* AltPrivSep interfaces are set up */
2827c478bd9Sstevel@tonic-gate 	aps_started = 1;
2837c478bd9Sstevel@tonic-gate 	return (pid);
2847c478bd9Sstevel@tonic-gate }
2857c478bd9Sstevel@tonic-gate 
2867c478bd9Sstevel@tonic-gate int
2877c478bd9Sstevel@tonic-gate altprivsep_get_pipe_fd(void)
2887c478bd9Sstevel@tonic-gate {
2897c478bd9Sstevel@tonic-gate 	return (pipe_fd);
2907c478bd9Sstevel@tonic-gate }
2917c478bd9Sstevel@tonic-gate 
2927c478bd9Sstevel@tonic-gate void
2937c478bd9Sstevel@tonic-gate altprivsep_rekey(int type, u_int32_t seq, void *ctxt)
2947c478bd9Sstevel@tonic-gate {
2957c478bd9Sstevel@tonic-gate 	Kex *kex = (Kex *)ctxt;
2967c478bd9Sstevel@tonic-gate 
2977c478bd9Sstevel@tonic-gate 	if (kex == NULL)
2987c478bd9Sstevel@tonic-gate 		fatal("Missing key exchange context in unprivileged process");
2997c478bd9Sstevel@tonic-gate 
3007c478bd9Sstevel@tonic-gate 	debug2("Forwarding re-key packet (%d) to monitor", type);
3017c478bd9Sstevel@tonic-gate 
3027c478bd9Sstevel@tonic-gate 	if (type != SSH2_MSG_NEWKEYS)
3037c478bd9Sstevel@tonic-gate 		if (!altprivsep_fwd_packet(type))
3047c478bd9Sstevel@tonic-gate 			fatal("Monitor not responding");
3057c478bd9Sstevel@tonic-gate 
3067c478bd9Sstevel@tonic-gate 	/* tell server_loop2() that we're re-keying */
3077c478bd9Sstevel@tonic-gate 	kex->done = 0;
3087c478bd9Sstevel@tonic-gate 
3097c478bd9Sstevel@tonic-gate 	/* NEWKEYS is special: get the new keys for client->server direction */
3107c478bd9Sstevel@tonic-gate 	if (type == SSH2_MSG_NEWKEYS) {
3117c478bd9Sstevel@tonic-gate 		debug2("Getting new inbound keystate from monitor");
3127c478bd9Sstevel@tonic-gate 		altprivsep_get_newkeys(MODE_IN);
3137c478bd9Sstevel@tonic-gate 		kex->done = 1;
3147c478bd9Sstevel@tonic-gate 	}
3157c478bd9Sstevel@tonic-gate }
3167c478bd9Sstevel@tonic-gate 
3177c478bd9Sstevel@tonic-gate void
318*dbe3f931Sjp161948 altprivsep_process_input(fd_set *rset)
3197c478bd9Sstevel@tonic-gate {
3207c478bd9Sstevel@tonic-gate 	void	*data;
3217c478bd9Sstevel@tonic-gate 	int	 type;
3227c478bd9Sstevel@tonic-gate 	u_int	 dlen;
3237c478bd9Sstevel@tonic-gate 
3247c478bd9Sstevel@tonic-gate 	debug2("Reading from pipe to monitor (%d)", pipe_fd);
3257c478bd9Sstevel@tonic-gate 
3267c478bd9Sstevel@tonic-gate 	if (pipe_fd == -1)
3277c478bd9Sstevel@tonic-gate 		return;
3287c478bd9Sstevel@tonic-gate 
3297c478bd9Sstevel@tonic-gate 	if (!FD_ISSET(pipe_fd, rset))
3307c478bd9Sstevel@tonic-gate 		return;
3317c478bd9Sstevel@tonic-gate 
3327c478bd9Sstevel@tonic-gate 	if ((type = altprivsep_packet_read()) == -1)
3337c478bd9Sstevel@tonic-gate 		fatal("Monitor not responding");
3347c478bd9Sstevel@tonic-gate 
3357c478bd9Sstevel@tonic-gate 	if (!compat20)
3367c478bd9Sstevel@tonic-gate 		return; /* shouldn't happen! but be safe */
3377c478bd9Sstevel@tonic-gate 
3387c478bd9Sstevel@tonic-gate 	if (type == 0)
3397c478bd9Sstevel@tonic-gate 		return;	/* EOF -- nothing to do here */
3407c478bd9Sstevel@tonic-gate 
3417c478bd9Sstevel@tonic-gate 	if (type >= SSH2_MSG_MAX)
3427c478bd9Sstevel@tonic-gate 		fatal("Received garbage from monitor");
3437c478bd9Sstevel@tonic-gate 
3447c478bd9Sstevel@tonic-gate 	debug2("Read packet type %d from pipe to monitor", (u_int)type);
3457c478bd9Sstevel@tonic-gate 
3467c478bd9Sstevel@tonic-gate 	if (type == SSH2_PRIV_MSG_ALTPRIVSEP)
3477c478bd9Sstevel@tonic-gate 		return; /* shouldn't happen! */
3487c478bd9Sstevel@tonic-gate 
3497c478bd9Sstevel@tonic-gate 	/* NEWKEYS is special: get the new keys for server->client direction */
3507c478bd9Sstevel@tonic-gate 	if (type == SSH2_MSG_NEWKEYS) {
3517c478bd9Sstevel@tonic-gate 		debug2("Getting new outbound keystate from monitor");
3527c478bd9Sstevel@tonic-gate 		packet_start(SSH2_MSG_NEWKEYS);
3537c478bd9Sstevel@tonic-gate 		packet_send();
3547c478bd9Sstevel@tonic-gate 		altprivsep_get_newkeys(MODE_OUT);
3557c478bd9Sstevel@tonic-gate 		return;
3567c478bd9Sstevel@tonic-gate 	}
3577c478bd9Sstevel@tonic-gate 
3587c478bd9Sstevel@tonic-gate 	data = altprivsep_packet_get_raw(&dlen);
3597c478bd9Sstevel@tonic-gate 
3607c478bd9Sstevel@tonic-gate 	packet_start((u_char)type);
3617c478bd9Sstevel@tonic-gate 
3627c478bd9Sstevel@tonic-gate 	if (data != NULL && dlen > 0)
3637c478bd9Sstevel@tonic-gate 		packet_put_raw(data, dlen);
3647c478bd9Sstevel@tonic-gate 
3657c478bd9Sstevel@tonic-gate 	packet_send();
3667c478bd9Sstevel@tonic-gate }
3677c478bd9Sstevel@tonic-gate 
3687c478bd9Sstevel@tonic-gate void
3697c478bd9Sstevel@tonic-gate altprivsep_do_monitor(Authctxt *authctxt, pid_t child_pid)
3707c478bd9Sstevel@tonic-gate {
3717c478bd9Sstevel@tonic-gate 	aps_monitor_loop(authctxt, pipe_fd, child_pid);
3727c478bd9Sstevel@tonic-gate }
3737c478bd9Sstevel@tonic-gate 
3747c478bd9Sstevel@tonic-gate int
3757c478bd9Sstevel@tonic-gate altprivsep_started(void)
3767c478bd9Sstevel@tonic-gate {
3777c478bd9Sstevel@tonic-gate 	return (aps_started);
3787c478bd9Sstevel@tonic-gate }
3797c478bd9Sstevel@tonic-gate 
3807c478bd9Sstevel@tonic-gate int
3817c478bd9Sstevel@tonic-gate altprivsep_is_monitor(void)
3827c478bd9Sstevel@tonic-gate {
3837c478bd9Sstevel@tonic-gate 	return (is_monitor);
3847c478bd9Sstevel@tonic-gate }
3857c478bd9Sstevel@tonic-gate 
3867c478bd9Sstevel@tonic-gate /*
3877c478bd9Sstevel@tonic-gate  * A fatal cleanup function to forcibly shutdown the connection socket
3887c478bd9Sstevel@tonic-gate  */
3897c478bd9Sstevel@tonic-gate void
3907c478bd9Sstevel@tonic-gate altprivsep_shutdown_sock(void *arg)
3917c478bd9Sstevel@tonic-gate {
3927c478bd9Sstevel@tonic-gate 	int sock;
3937c478bd9Sstevel@tonic-gate 
3947c478bd9Sstevel@tonic-gate 	if (arg == NULL)
3957c478bd9Sstevel@tonic-gate 		return;
3967c478bd9Sstevel@tonic-gate 
3977c478bd9Sstevel@tonic-gate 	sock = *(int *)arg;
3987c478bd9Sstevel@tonic-gate 
3997c478bd9Sstevel@tonic-gate 	(void) shutdown(sock, SHUT_RDWR);
4007c478bd9Sstevel@tonic-gate }
4017c478bd9Sstevel@tonic-gate 
4027c478bd9Sstevel@tonic-gate /* Calls _to_ monitor from unprivileged process */
4037c478bd9Sstevel@tonic-gate static
4047c478bd9Sstevel@tonic-gate int
4057c478bd9Sstevel@tonic-gate altprivsep_fwd_packet(u_char type)
4067c478bd9Sstevel@tonic-gate {
4077c478bd9Sstevel@tonic-gate 	u_int len;
4087c478bd9Sstevel@tonic-gate 	void  *data;
4097c478bd9Sstevel@tonic-gate 
4107c478bd9Sstevel@tonic-gate 	altprivsep_packet_start(type);
4117c478bd9Sstevel@tonic-gate 	data = packet_get_raw(&len);
4127c478bd9Sstevel@tonic-gate 	altprivsep_packet_put_raw(data, len);
4137c478bd9Sstevel@tonic-gate 
4147c478bd9Sstevel@tonic-gate 	/* packet_send()s any replies from the monitor to the client */
4157c478bd9Sstevel@tonic-gate 	return (altprivsep_packet_send());
4167c478bd9Sstevel@tonic-gate }
4177c478bd9Sstevel@tonic-gate 
4187c478bd9Sstevel@tonic-gate extern Newkeys *current_keys[MODE_MAX];
4197c478bd9Sstevel@tonic-gate 
4207c478bd9Sstevel@tonic-gate /* To be called from packet.c:set_newkeys() before referencing current_keys */
4217c478bd9Sstevel@tonic-gate void
4227c478bd9Sstevel@tonic-gate altprivsep_get_newkeys(enum kex_modes mode)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate 	Newkeys	*newkeys;
4257c478bd9Sstevel@tonic-gate 	Comp	*comp;
4267c478bd9Sstevel@tonic-gate 	Enc	*enc;
4277c478bd9Sstevel@tonic-gate 	Mac	*mac;
4287c478bd9Sstevel@tonic-gate 	u_int	 len;
4297c478bd9Sstevel@tonic-gate 
4307c478bd9Sstevel@tonic-gate 	if (!altprivsep_started())
4317c478bd9Sstevel@tonic-gate 		return;
4327c478bd9Sstevel@tonic-gate 
4337c478bd9Sstevel@tonic-gate 	if (altprivsep_is_monitor())
4347c478bd9Sstevel@tonic-gate 		return; /* shouldn't happen */
4357c478bd9Sstevel@tonic-gate 
4367c478bd9Sstevel@tonic-gate 	/* request new keys */
4377c478bd9Sstevel@tonic-gate 	altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
4387c478bd9Sstevel@tonic-gate 	altprivsep_packet_put_char(APS_MSG_NEWKEYS_REQ);
4397c478bd9Sstevel@tonic-gate 	altprivsep_packet_put_int((u_int)mode);
4407c478bd9Sstevel@tonic-gate 	altprivsep_packet_send();
4417c478bd9Sstevel@tonic-gate 	altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
4427c478bd9Sstevel@tonic-gate 	if (altprivsep_packet_get_char() != APS_MSG_NEWKEYS_REP)
4437c478bd9Sstevel@tonic-gate 		fatal("Received garbage from monitor during re-keying");
4447c478bd9Sstevel@tonic-gate 
4457c478bd9Sstevel@tonic-gate 	newkeys = xmalloc(sizeof (*newkeys));
4467c478bd9Sstevel@tonic-gate 	memset(newkeys, 0, sizeof (*newkeys));
4477c478bd9Sstevel@tonic-gate 
4487c478bd9Sstevel@tonic-gate 	enc = &newkeys->enc;
4497c478bd9Sstevel@tonic-gate 	mac = &newkeys->mac;
4507c478bd9Sstevel@tonic-gate 	comp = &newkeys->comp;
4517c478bd9Sstevel@tonic-gate 
4527c478bd9Sstevel@tonic-gate 	/* Cipher name, key, IV */
4537c478bd9Sstevel@tonic-gate 	enc->name = altprivsep_packet_get_string(NULL);
4547c478bd9Sstevel@tonic-gate 	if ((enc->cipher = cipher_by_name(enc->name)) == NULL)
4557c478bd9Sstevel@tonic-gate 		fatal("Monitor negotiated an unknown cipher during re-key");
4567c478bd9Sstevel@tonic-gate 
4577c478bd9Sstevel@tonic-gate 	enc->key = altprivsep_packet_get_string(&enc->key_len);
4587c478bd9Sstevel@tonic-gate 	enc->iv = altprivsep_packet_get_string(&enc->block_size);
4597c478bd9Sstevel@tonic-gate 
4607c478bd9Sstevel@tonic-gate 	/* MAC name */
4617c478bd9Sstevel@tonic-gate 	mac->name = altprivsep_packet_get_string(NULL);
4627c478bd9Sstevel@tonic-gate 	if (mac_init(mac, mac->name) < 0)
4637c478bd9Sstevel@tonic-gate 		fatal("Monitor negotiated an unknown MAC algorithm "
4647c478bd9Sstevel@tonic-gate 			"during re-key");
4657c478bd9Sstevel@tonic-gate 
4667c478bd9Sstevel@tonic-gate 	mac->key = altprivsep_packet_get_string(&len);
4677c478bd9Sstevel@tonic-gate 	if (len > mac->key_len)
4687c478bd9Sstevel@tonic-gate 		fatal("%s: bad mac key length: %d > %d", __func__, len,
4697c478bd9Sstevel@tonic-gate 			mac->key_len);
4707c478bd9Sstevel@tonic-gate 
4717c478bd9Sstevel@tonic-gate 	/* Compression algorithm name */
4727c478bd9Sstevel@tonic-gate 	comp->name = altprivsep_packet_get_string(NULL);
4737c478bd9Sstevel@tonic-gate 	if (strcmp(comp->name, "zlib") != 0 && strcmp(comp->name, "none") != 0)
4747c478bd9Sstevel@tonic-gate 		fatal("Monitor negotiated an unknown compression "
4757c478bd9Sstevel@tonic-gate 			"algorithm during re-key");
4767c478bd9Sstevel@tonic-gate 
4777c478bd9Sstevel@tonic-gate 	comp->type = 0;
4787c478bd9Sstevel@tonic-gate 	comp->enabled = 0; /* forces compression re-init, as per-spec */
4797c478bd9Sstevel@tonic-gate 	if (strcmp(comp->name, "zlib") == 0)
4807c478bd9Sstevel@tonic-gate 		comp->type = 1;
4817c478bd9Sstevel@tonic-gate 
4827c478bd9Sstevel@tonic-gate 	/*
4837c478bd9Sstevel@tonic-gate 	 * Now install new keys
4847c478bd9Sstevel@tonic-gate 	 *
4857c478bd9Sstevel@tonic-gate 	 * For now abuse kex.c/packet.c non-interfaces.  Someday, when
4867c478bd9Sstevel@tonic-gate 	 * the many internal interfaces are parametrized, made reentrant
4877c478bd9Sstevel@tonic-gate 	 * and thread-safe, made more consistent, and when necessary-but-
4887c478bd9Sstevel@tonic-gate 	 * currently-missing interfaces are added then this bit of
4897c478bd9Sstevel@tonic-gate 	 * ugliness can be revisited.
4907c478bd9Sstevel@tonic-gate 	 *
4917c478bd9Sstevel@tonic-gate 	 * The ugliness is in the set_newkeys(), its name and the lack
4927c478bd9Sstevel@tonic-gate 	 * of a (Newkeys *) parameter, which forces us to pass the
4937c478bd9Sstevel@tonic-gate 	 * newkeys through current_keys[mode].  But this saves us some
4947c478bd9Sstevel@tonic-gate 	 * lines of code for now, though not comments.
4957c478bd9Sstevel@tonic-gate 	 *
4967c478bd9Sstevel@tonic-gate 	 * Also, we've abused, in the code above, knowledge of what
4977c478bd9Sstevel@tonic-gate 	 * set_newkeys() expects the current_keys[mode] to contain.
4987c478bd9Sstevel@tonic-gate 	 */
4997c478bd9Sstevel@tonic-gate 	current_keys[mode] = newkeys;
5007c478bd9Sstevel@tonic-gate 	set_newkeys(mode);
5017c478bd9Sstevel@tonic-gate 
5027c478bd9Sstevel@tonic-gate }
5037c478bd9Sstevel@tonic-gate 
5047c478bd9Sstevel@tonic-gate void
5057c478bd9Sstevel@tonic-gate altprivsep_record_login(pid_t pid, const char *ttyname)
5067c478bd9Sstevel@tonic-gate {
5077c478bd9Sstevel@tonic-gate 	altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
5087c478bd9Sstevel@tonic-gate 	altprivsep_packet_put_char(APS_MSG_RECORD_LOGIN);
5097c478bd9Sstevel@tonic-gate 	altprivsep_packet_put_int(pid);
5107c478bd9Sstevel@tonic-gate 	altprivsep_packet_put_cstring(ttyname);
5117c478bd9Sstevel@tonic-gate 	altprivsep_packet_send();
5127c478bd9Sstevel@tonic-gate 	altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
5137c478bd9Sstevel@tonic-gate }
5147c478bd9Sstevel@tonic-gate 
5157c478bd9Sstevel@tonic-gate void
5167c478bd9Sstevel@tonic-gate altprivsep_record_logout(pid_t pid)
5177c478bd9Sstevel@tonic-gate {
5187c478bd9Sstevel@tonic-gate 	altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
5197c478bd9Sstevel@tonic-gate 	altprivsep_packet_put_char(APS_MSG_RECORD_LOGOUT);
5207c478bd9Sstevel@tonic-gate 	altprivsep_packet_put_int(pid);
5217c478bd9Sstevel@tonic-gate 	altprivsep_packet_send();
5227c478bd9Sstevel@tonic-gate 	altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
5237c478bd9Sstevel@tonic-gate }
5247c478bd9Sstevel@tonic-gate 
525*dbe3f931Sjp161948 void
526*dbe3f931Sjp161948 altprivsep_start_rekex(void)
527*dbe3f931Sjp161948 {
528*dbe3f931Sjp161948 	altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
529*dbe3f931Sjp161948 	altprivsep_packet_put_char(APS_MSG_START_REKEX);
530*dbe3f931Sjp161948 	altprivsep_packet_send();
531*dbe3f931Sjp161948 	altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
532*dbe3f931Sjp161948 }
533*dbe3f931Sjp161948 
5347c478bd9Sstevel@tonic-gate static void aps_send_newkeys(void);
5357c478bd9Sstevel@tonic-gate 
5367c478bd9Sstevel@tonic-gate /* Monitor side dispatch handler for SSH2_PRIV_MSG_ALTPRIVSEP */
5377c478bd9Sstevel@tonic-gate /* ARGSUSED */
5387c478bd9Sstevel@tonic-gate void
5397c478bd9Sstevel@tonic-gate aps_input_altpriv_msg(int type, u_int32_t seq, void *ctxt)
5407c478bd9Sstevel@tonic-gate {
5417c478bd9Sstevel@tonic-gate 	u_char req_type;
5427c478bd9Sstevel@tonic-gate 
5437c478bd9Sstevel@tonic-gate 	req_type = packet_get_char();
5447c478bd9Sstevel@tonic-gate 
5457c478bd9Sstevel@tonic-gate 	switch (req_type) {
5467c478bd9Sstevel@tonic-gate 	case APS_MSG_NEWKEYS_REQ:
5477c478bd9Sstevel@tonic-gate 		aps_send_newkeys();
5487c478bd9Sstevel@tonic-gate 		break;
5497c478bd9Sstevel@tonic-gate 	case APS_MSG_RECORD_LOGIN:
5507c478bd9Sstevel@tonic-gate 		aps_record_login();
5517c478bd9Sstevel@tonic-gate 		break;
5527c478bd9Sstevel@tonic-gate 	case APS_MSG_RECORD_LOGOUT:
5537c478bd9Sstevel@tonic-gate 		aps_record_logout();
5547c478bd9Sstevel@tonic-gate 		break;
555*dbe3f931Sjp161948 	case APS_MSG_START_REKEX:
556*dbe3f931Sjp161948 		aps_start_rekex();
557*dbe3f931Sjp161948 		break;
5587c478bd9Sstevel@tonic-gate 	default:
5597c478bd9Sstevel@tonic-gate 		break;
5607c478bd9Sstevel@tonic-gate 	}
5617c478bd9Sstevel@tonic-gate }
5627c478bd9Sstevel@tonic-gate 
5637c478bd9Sstevel@tonic-gate /* Monitor-side handlers for APS_MSG_* */
5647c478bd9Sstevel@tonic-gate static
5657c478bd9Sstevel@tonic-gate void
5667c478bd9Sstevel@tonic-gate aps_send_newkeys(void)
5677c478bd9Sstevel@tonic-gate {
5687c478bd9Sstevel@tonic-gate 	Newkeys *newkeys;
5697c478bd9Sstevel@tonic-gate 	Enc *enc;
5707c478bd9Sstevel@tonic-gate 	Mac *mac;
5717c478bd9Sstevel@tonic-gate 	Comp *comp;
5727c478bd9Sstevel@tonic-gate 	enum kex_modes mode;
5737c478bd9Sstevel@tonic-gate 
5747c478bd9Sstevel@tonic-gate 	/* get direction for which newkeys are wanted */
5757c478bd9Sstevel@tonic-gate 	mode = (enum kex_modes) packet_get_int();
5767c478bd9Sstevel@tonic-gate 	packet_check_eom();
5777c478bd9Sstevel@tonic-gate 
5787c478bd9Sstevel@tonic-gate 	/* get those newkeys */
5797c478bd9Sstevel@tonic-gate 	newkeys = kex_get_newkeys(mode);
5807c478bd9Sstevel@tonic-gate 	enc = &newkeys->enc;
5817c478bd9Sstevel@tonic-gate 	mac = &newkeys->mac;
5827c478bd9Sstevel@tonic-gate 	comp = &newkeys->comp;
5837c478bd9Sstevel@tonic-gate 
5847c478bd9Sstevel@tonic-gate 	/*
5857c478bd9Sstevel@tonic-gate 	 * Negotiated algorithms, client->server and server->client, for
5867c478bd9Sstevel@tonic-gate 	 * cipher, mac and compression.
5877c478bd9Sstevel@tonic-gate 	 */
5887c478bd9Sstevel@tonic-gate 	packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
5897c478bd9Sstevel@tonic-gate 	packet_put_char(APS_MSG_NEWKEYS_REP);
5907c478bd9Sstevel@tonic-gate 	packet_put_cstring(enc->name);
5917c478bd9Sstevel@tonic-gate 	packet_put_string(enc->key, enc->key_len);
5927c478bd9Sstevel@tonic-gate 	packet_put_string(enc->iv, enc->block_size);
5937c478bd9Sstevel@tonic-gate 	packet_put_cstring(mac->name);
5947c478bd9Sstevel@tonic-gate 	packet_put_string(mac->key, mac->key_len);
5957c478bd9Sstevel@tonic-gate 	packet_put_cstring(comp->name);
5967c478bd9Sstevel@tonic-gate 
5977c478bd9Sstevel@tonic-gate 	packet_send();
5989a8058b5Sjp161948 	free_keys(newkeys);
5997c478bd9Sstevel@tonic-gate }
6007c478bd9Sstevel@tonic-gate 
6017c478bd9Sstevel@tonic-gate struct _aps_login_rec {
6027c478bd9Sstevel@tonic-gate 	pid_t			lr_pid;
6037c478bd9Sstevel@tonic-gate 	char			*lr_tty;
6047c478bd9Sstevel@tonic-gate 	struct _aps_login_rec	*next;
6057c478bd9Sstevel@tonic-gate };
6067c478bd9Sstevel@tonic-gate 
6077c478bd9Sstevel@tonic-gate typedef struct _aps_login_rec aps_login_rec;
6087c478bd9Sstevel@tonic-gate 
6097c478bd9Sstevel@tonic-gate static aps_login_rec *aps_login_list = NULL;
6107c478bd9Sstevel@tonic-gate 
6117c478bd9Sstevel@tonic-gate static
6127c478bd9Sstevel@tonic-gate void
6137c478bd9Sstevel@tonic-gate aps_record_login(void)
6147c478bd9Sstevel@tonic-gate {
6157c478bd9Sstevel@tonic-gate 	aps_login_rec	*new_rec;
6167c478bd9Sstevel@tonic-gate 	struct stat	 sbuf;
6177c478bd9Sstevel@tonic-gate 	size_t		 proc_path_len;
6187c478bd9Sstevel@tonic-gate 	char		*proc_path;
6197c478bd9Sstevel@tonic-gate 
6207c478bd9Sstevel@tonic-gate 	new_rec = xmalloc(sizeof (aps_login_rec));
6217c478bd9Sstevel@tonic-gate 	memset(new_rec, 0, sizeof (aps_login_rec));
6227c478bd9Sstevel@tonic-gate 
6237c478bd9Sstevel@tonic-gate 	new_rec->lr_pid = packet_get_int();
6247c478bd9Sstevel@tonic-gate 	new_rec->lr_tty = packet_get_string(NULL);
6257c478bd9Sstevel@tonic-gate 
6267c478bd9Sstevel@tonic-gate 	proc_path_len = snprintf(NULL, 0, "/proc/%d", new_rec->lr_pid);
6277c478bd9Sstevel@tonic-gate 	proc_path = xmalloc(proc_path_len + 1);
6287c478bd9Sstevel@tonic-gate 	(void) snprintf(proc_path, proc_path_len + 1, "/proc/%d",
6297c478bd9Sstevel@tonic-gate 			new_rec->lr_pid);
6307c478bd9Sstevel@tonic-gate 
6317c478bd9Sstevel@tonic-gate 	if (stat(proc_path, &sbuf) ||
6327c478bd9Sstevel@tonic-gate 	    sbuf.st_uid != xxx_authctxt->pw->pw_uid ||
6337c478bd9Sstevel@tonic-gate 	    stat(new_rec->lr_tty, &sbuf) < 0 ||
6347c478bd9Sstevel@tonic-gate 	    sbuf.st_uid != xxx_authctxt->pw->pw_uid) {
6357c478bd9Sstevel@tonic-gate 		debug2("Spurious record_login request from unprivileged sshd");
6367c478bd9Sstevel@tonic-gate 		xfree(proc_path);
6377c478bd9Sstevel@tonic-gate 		xfree(new_rec->lr_tty);
6387c478bd9Sstevel@tonic-gate 		xfree(new_rec);
6397c478bd9Sstevel@tonic-gate 		return;
6407c478bd9Sstevel@tonic-gate 	}
6417c478bd9Sstevel@tonic-gate 
6427c478bd9Sstevel@tonic-gate 	/* Insert new record on list */
6437c478bd9Sstevel@tonic-gate 	new_rec->next = aps_login_list;
6447c478bd9Sstevel@tonic-gate 	aps_login_list = new_rec;
6457c478bd9Sstevel@tonic-gate 
6467c478bd9Sstevel@tonic-gate 	record_login(new_rec->lr_pid, new_rec->lr_tty, NULL,
6477c478bd9Sstevel@tonic-gate 		xxx_authctxt->user);
6487c478bd9Sstevel@tonic-gate 
6497c478bd9Sstevel@tonic-gate 	packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
6507c478bd9Sstevel@tonic-gate 	packet_send();
6517c478bd9Sstevel@tonic-gate 
6527c478bd9Sstevel@tonic-gate 	xfree(proc_path);
6537c478bd9Sstevel@tonic-gate }
6547c478bd9Sstevel@tonic-gate 
6557c478bd9Sstevel@tonic-gate static
6567c478bd9Sstevel@tonic-gate void
6577c478bd9Sstevel@tonic-gate aps_record_logout(void)
6587c478bd9Sstevel@tonic-gate {
6597c478bd9Sstevel@tonic-gate 	aps_login_rec	**p, *q;
6607c478bd9Sstevel@tonic-gate 	pid_t		 pid;
6617c478bd9Sstevel@tonic-gate 
6627c478bd9Sstevel@tonic-gate 	pid = packet_get_int();
6637c478bd9Sstevel@tonic-gate 	packet_check_eom();
6647c478bd9Sstevel@tonic-gate 
6657c478bd9Sstevel@tonic-gate 	for (p = &aps_login_list; *p != NULL; p = &q->next) {
6667c478bd9Sstevel@tonic-gate 		q = *p;
6677c478bd9Sstevel@tonic-gate 		if (q->lr_pid == pid) {
6687c478bd9Sstevel@tonic-gate 			record_logout(q->lr_pid, q->lr_tty, NULL,
6697c478bd9Sstevel@tonic-gate 				xxx_authctxt->user);
6707c478bd9Sstevel@tonic-gate 
6717c478bd9Sstevel@tonic-gate 			/* dequeue */
6727c478bd9Sstevel@tonic-gate 			*p = q->next;
6737c478bd9Sstevel@tonic-gate 			xfree(q->lr_tty);
6747c478bd9Sstevel@tonic-gate 			xfree(q);
6757c478bd9Sstevel@tonic-gate 			break;
6767c478bd9Sstevel@tonic-gate 		}
6777c478bd9Sstevel@tonic-gate 	}
6787c478bd9Sstevel@tonic-gate 
6797c478bd9Sstevel@tonic-gate 	packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
6807c478bd9Sstevel@tonic-gate 	packet_send();
6817c478bd9Sstevel@tonic-gate }
6827c478bd9Sstevel@tonic-gate 
683*dbe3f931Sjp161948 static
684*dbe3f931Sjp161948 void
685*dbe3f931Sjp161948 aps_start_rekex(void)
686*dbe3f931Sjp161948 {
687*dbe3f931Sjp161948 	/*
688*dbe3f931Sjp161948 	 * Send confirmation. We could implement it without that but it doesn't
689*dbe3f931Sjp161948 	 * bring any harm to do that and we are consistent with other subtypes
690*dbe3f931Sjp161948 	 * of our private SSH2_PRIV_MSG_ALTPRIVSEP message type.
691*dbe3f931Sjp161948 	 */
692*dbe3f931Sjp161948 	packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
693*dbe3f931Sjp161948 	packet_send();
694*dbe3f931Sjp161948 
695*dbe3f931Sjp161948 	/*
696*dbe3f931Sjp161948 	 * KEX_INIT message could be the one that reached the limit. In that
697*dbe3f931Sjp161948 	 * case, it was already forwarded to us from the unnprivileged child,
698*dbe3f931Sjp161948 	 * and maybe even acted upon. Obviously we must not send another
699*dbe3f931Sjp161948 	 * KEX_INIT message.
700*dbe3f931Sjp161948 	 */
701*dbe3f931Sjp161948 	if (!(xxx_kex->flags & KEX_INIT_SENT))
702*dbe3f931Sjp161948 		kex_send_kexinit(xxx_kex);
703*dbe3f931Sjp161948 	else
704*dbe3f931Sjp161948 		debug2("rekeying already in progress");
705*dbe3f931Sjp161948 }
706*dbe3f931Sjp161948 
707*dbe3f931Sjp161948 
7087c478bd9Sstevel@tonic-gate /* Utilities for communication with the monitor */
7097c478bd9Sstevel@tonic-gate static
7107c478bd9Sstevel@tonic-gate void
7117c478bd9Sstevel@tonic-gate altprivsep_packet_start(u_char type)
7127c478bd9Sstevel@tonic-gate {
7137c478bd9Sstevel@tonic-gate 	buffer_clear(&to_monitor);
7147c478bd9Sstevel@tonic-gate 	buffer_put_char(&to_monitor, type);
7157c478bd9Sstevel@tonic-gate }
7167c478bd9Sstevel@tonic-gate static
7177c478bd9Sstevel@tonic-gate void
7187c478bd9Sstevel@tonic-gate altprivsep_packet_put_char(int ch)
7197c478bd9Sstevel@tonic-gate {
7207c478bd9Sstevel@tonic-gate 	buffer_put_char(&to_monitor, ch);
7217c478bd9Sstevel@tonic-gate }
7227c478bd9Sstevel@tonic-gate static
7237c478bd9Sstevel@tonic-gate void
7247c478bd9Sstevel@tonic-gate altprivsep_packet_put_int(u_int value)
7257c478bd9Sstevel@tonic-gate {
7267c478bd9Sstevel@tonic-gate 	buffer_put_int(&to_monitor, value);
7277c478bd9Sstevel@tonic-gate }
7287c478bd9Sstevel@tonic-gate static
7297c478bd9Sstevel@tonic-gate void
7307c478bd9Sstevel@tonic-gate altprivsep_packet_put_cstring(const char *str)
7317c478bd9Sstevel@tonic-gate {
7327c478bd9Sstevel@tonic-gate 	buffer_put_cstring(&to_monitor, str);
7337c478bd9Sstevel@tonic-gate }
7347c478bd9Sstevel@tonic-gate static
7357c478bd9Sstevel@tonic-gate void
7367c478bd9Sstevel@tonic-gate altprivsep_packet_put_raw(const void *buf, u_int len)
7377c478bd9Sstevel@tonic-gate {
7387c478bd9Sstevel@tonic-gate 	buffer_append(&to_monitor, buf, len);
7397c478bd9Sstevel@tonic-gate }
7407c478bd9Sstevel@tonic-gate 
7417c478bd9Sstevel@tonic-gate /*
7427c478bd9Sstevel@tonic-gate  * Send a monitor packet to the monitor.  This function is blocking.
7437c478bd9Sstevel@tonic-gate  *
7447c478bd9Sstevel@tonic-gate  * Returns -1 if the monitor pipe has been closed earlier, fatal()s if
7457c478bd9Sstevel@tonic-gate  * there's any other problems.
7467c478bd9Sstevel@tonic-gate  */
7477c478bd9Sstevel@tonic-gate static
7487c478bd9Sstevel@tonic-gate int
7497c478bd9Sstevel@tonic-gate altprivsep_packet_send(void)
7507c478bd9Sstevel@tonic-gate {
7517c478bd9Sstevel@tonic-gate 	ssize_t len;
7527c478bd9Sstevel@tonic-gate 	u_int32_t plen;	/* packet length */
7537c478bd9Sstevel@tonic-gate 	u_char	plen_buf[sizeof (plen)];
7547c478bd9Sstevel@tonic-gate 	u_char padlen;	/* padding length */
7557c478bd9Sstevel@tonic-gate 	fd_set *setp;
7567c478bd9Sstevel@tonic-gate 
7577c478bd9Sstevel@tonic-gate 	if (pipe_fd == -1)
7587c478bd9Sstevel@tonic-gate 		return (-1);
7597c478bd9Sstevel@tonic-gate 
7607c478bd9Sstevel@tonic-gate 	if ((plen = buffer_len(&to_monitor)) == 0)
7617c478bd9Sstevel@tonic-gate 		return (0);
7627c478bd9Sstevel@tonic-gate 
7637c478bd9Sstevel@tonic-gate 	/*
7647c478bd9Sstevel@tonic-gate 	 * We talk the SSHv2 binary packet protocol to the monitor,
7657c478bd9Sstevel@tonic-gate 	 * using the none cipher, mac and compression algorithms.
7667c478bd9Sstevel@tonic-gate 	 *
7677c478bd9Sstevel@tonic-gate 	 * But, interestingly, the none cipher has a block size of 8
7687c478bd9Sstevel@tonic-gate 	 * bytes, thus we must pad the packet.
7697c478bd9Sstevel@tonic-gate 	 *
7707c478bd9Sstevel@tonic-gate 	 * Also, encryption includes the packet length, so the padding
7717c478bd9Sstevel@tonic-gate 	 * must account for that field.  I.e., (sizeof (packet length) +
7727c478bd9Sstevel@tonic-gate 	 * sizeof (padding length) + packet length + padding length) %
7737c478bd9Sstevel@tonic-gate 	 * block_size must == 0.
7747c478bd9Sstevel@tonic-gate 	 *
7757c478bd9Sstevel@tonic-gate 	 * Also, there must be at least four (4) bytes of padding.
7767c478bd9Sstevel@tonic-gate 	 */
7777c478bd9Sstevel@tonic-gate 	padlen = (8 - ((plen + sizeof (plen) + sizeof (padlen)) % 8)) % 8;
7787c478bd9Sstevel@tonic-gate 	if (padlen < 4)
7797c478bd9Sstevel@tonic-gate 		padlen += 8;
7807c478bd9Sstevel@tonic-gate 
7817c478bd9Sstevel@tonic-gate 	/* packet length counts padding and padding length field */
7827c478bd9Sstevel@tonic-gate 	plen += padlen + sizeof (padlen);
7837c478bd9Sstevel@tonic-gate 
7847c478bd9Sstevel@tonic-gate 	PUT_32BIT(plen_buf, plen);
7857c478bd9Sstevel@tonic-gate 
7867c478bd9Sstevel@tonic-gate 	setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
7877c478bd9Sstevel@tonic-gate 	memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
7887c478bd9Sstevel@tonic-gate 	FD_SET(pipe_fd, setp);
7897c478bd9Sstevel@tonic-gate 
7907c478bd9Sstevel@tonic-gate 	while (select(pipe_fd + 1, NULL, setp, NULL, NULL) == -1) {
7917c478bd9Sstevel@tonic-gate 		if (errno == EAGAIN || errno == EINTR)
7927c478bd9Sstevel@tonic-gate 			continue;
7937c478bd9Sstevel@tonic-gate 		else
7947c478bd9Sstevel@tonic-gate 			goto pipe_gone;
7957c478bd9Sstevel@tonic-gate 	}
7967c478bd9Sstevel@tonic-gate 
7977c478bd9Sstevel@tonic-gate 	xfree(setp);
7987c478bd9Sstevel@tonic-gate 
7997c478bd9Sstevel@tonic-gate 	/* packet length field */
8007c478bd9Sstevel@tonic-gate 	len = atomicio(write, pipe_fd, plen_buf, sizeof (plen));
8017c478bd9Sstevel@tonic-gate 
8027c478bd9Sstevel@tonic-gate 	if (len != sizeof (plen))
8037c478bd9Sstevel@tonic-gate 		goto pipe_gone;
8047c478bd9Sstevel@tonic-gate 
8057c478bd9Sstevel@tonic-gate 	/* padding length field */
8067c478bd9Sstevel@tonic-gate 	len = atomicio(write, pipe_fd, &padlen, sizeof (padlen));
8077c478bd9Sstevel@tonic-gate 
8087c478bd9Sstevel@tonic-gate 	if (len != sizeof (padlen))
8097c478bd9Sstevel@tonic-gate 		goto pipe_gone;
8107c478bd9Sstevel@tonic-gate 
8117c478bd9Sstevel@tonic-gate 	len = atomicio(write, pipe_fd, buffer_ptr(&to_monitor), plen - 1);
8127c478bd9Sstevel@tonic-gate 
8137c478bd9Sstevel@tonic-gate 	if (len != (plen - 1))
8147c478bd9Sstevel@tonic-gate 		goto pipe_gone;
8157c478bd9Sstevel@tonic-gate 
8167c478bd9Sstevel@tonic-gate 	buffer_clear(&to_monitor);
8177c478bd9Sstevel@tonic-gate 
8187c478bd9Sstevel@tonic-gate 	return (1);
8197c478bd9Sstevel@tonic-gate 
8207c478bd9Sstevel@tonic-gate pipe_gone:
8217c478bd9Sstevel@tonic-gate 
8227c478bd9Sstevel@tonic-gate 	(void) close(pipe_fd);
8237c478bd9Sstevel@tonic-gate 
8247c478bd9Sstevel@tonic-gate 	pipe_fd = -1;
8257c478bd9Sstevel@tonic-gate 
8267c478bd9Sstevel@tonic-gate 	fatal("Monitor not responding");
8277c478bd9Sstevel@tonic-gate 
8287c478bd9Sstevel@tonic-gate 	/* NOTREACHED */
8297c478bd9Sstevel@tonic-gate 	return (0);
8307c478bd9Sstevel@tonic-gate }
8317c478bd9Sstevel@tonic-gate 
8327c478bd9Sstevel@tonic-gate /*
8337c478bd9Sstevel@tonic-gate  * Read a monitor packet from the monitor.  This function is blocking.
8347c478bd9Sstevel@tonic-gate  */
8357c478bd9Sstevel@tonic-gate static
8367c478bd9Sstevel@tonic-gate int
8377c478bd9Sstevel@tonic-gate altprivsep_packet_read(void)
8387c478bd9Sstevel@tonic-gate {
8397c478bd9Sstevel@tonic-gate 	ssize_t len = -1;
8407c478bd9Sstevel@tonic-gate 	u_int32_t plen;
8417c478bd9Sstevel@tonic-gate 	u_char plen_buf[sizeof (plen)];
8427c478bd9Sstevel@tonic-gate 	u_char padlen;
8437c478bd9Sstevel@tonic-gate 	fd_set *setp;
8447c478bd9Sstevel@tonic-gate 
8457c478bd9Sstevel@tonic-gate 	if (pipe_fd == -1)
8467c478bd9Sstevel@tonic-gate 		return (-1);
8477c478bd9Sstevel@tonic-gate 
8487c478bd9Sstevel@tonic-gate 	setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
8497c478bd9Sstevel@tonic-gate 	memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
8507c478bd9Sstevel@tonic-gate 	FD_SET(pipe_fd, setp);
8517c478bd9Sstevel@tonic-gate 
8527c478bd9Sstevel@tonic-gate 	while (select(pipe_fd + 1, setp, NULL, NULL, NULL) == -1) {
8537c478bd9Sstevel@tonic-gate 		if (errno == EAGAIN || errno == EINTR)
8547c478bd9Sstevel@tonic-gate 			continue;
8557c478bd9Sstevel@tonic-gate 		else
8567c478bd9Sstevel@tonic-gate 			goto pipe_gone;
8577c478bd9Sstevel@tonic-gate 	}
8587c478bd9Sstevel@tonic-gate 
8597c478bd9Sstevel@tonic-gate 	xfree(setp);
8607c478bd9Sstevel@tonic-gate 
8617c478bd9Sstevel@tonic-gate 	/* packet length field */
8627c478bd9Sstevel@tonic-gate 	len = atomicio(read, pipe_fd, plen_buf, sizeof (plen));
8637c478bd9Sstevel@tonic-gate 
8647c478bd9Sstevel@tonic-gate 	plen = GET_32BIT(plen_buf);
8657c478bd9Sstevel@tonic-gate 
8667c478bd9Sstevel@tonic-gate 	if (len != sizeof (plen))
8677c478bd9Sstevel@tonic-gate 		goto pipe_gone;
8687c478bd9Sstevel@tonic-gate 
8697c478bd9Sstevel@tonic-gate 	/* padding length field */
8707c478bd9Sstevel@tonic-gate 	len = atomicio(read, pipe_fd, &padlen, sizeof (padlen));
8717c478bd9Sstevel@tonic-gate 
8727c478bd9Sstevel@tonic-gate 	if (len != sizeof (padlen))
8737c478bd9Sstevel@tonic-gate 		goto pipe_gone;
8747c478bd9Sstevel@tonic-gate 
8757c478bd9Sstevel@tonic-gate 	plen -= sizeof (padlen);
8767c478bd9Sstevel@tonic-gate 
8777c478bd9Sstevel@tonic-gate 	buffer_clear(&from_monitor);
8787c478bd9Sstevel@tonic-gate 	buffer_append_space(&from_monitor, plen);
8797c478bd9Sstevel@tonic-gate 
8807c478bd9Sstevel@tonic-gate 	/* packet data + padding */
8817c478bd9Sstevel@tonic-gate 	len = atomicio(read, pipe_fd, buffer_ptr(&from_monitor), plen);
8827c478bd9Sstevel@tonic-gate 
8837c478bd9Sstevel@tonic-gate 	if (len != plen)
8847c478bd9Sstevel@tonic-gate 		goto pipe_gone;
8857c478bd9Sstevel@tonic-gate 
8867c478bd9Sstevel@tonic-gate 	/* remove padding */
8877c478bd9Sstevel@tonic-gate 	if (padlen > 0)
8887c478bd9Sstevel@tonic-gate 		buffer_consume_end(&from_monitor, padlen);
8897c478bd9Sstevel@tonic-gate 
8907c478bd9Sstevel@tonic-gate 	/* packet type */
8917c478bd9Sstevel@tonic-gate 	return (buffer_get_char(&from_monitor));
8927c478bd9Sstevel@tonic-gate 
8937c478bd9Sstevel@tonic-gate pipe_gone:
8947c478bd9Sstevel@tonic-gate 
8957c478bd9Sstevel@tonic-gate 	(void) close(pipe_fd);
8967c478bd9Sstevel@tonic-gate 
8977c478bd9Sstevel@tonic-gate 	pipe_fd = -1;
8987c478bd9Sstevel@tonic-gate 
8997c478bd9Sstevel@tonic-gate 	if (len < 0)
9007c478bd9Sstevel@tonic-gate 		fatal("Monitor not responding");
9017c478bd9Sstevel@tonic-gate 
9027c478bd9Sstevel@tonic-gate 	debug2("Monitor pipe closed by monitor");
9037c478bd9Sstevel@tonic-gate 	return (0);
9047c478bd9Sstevel@tonic-gate }
9057c478bd9Sstevel@tonic-gate 
9067c478bd9Sstevel@tonic-gate static
9077c478bd9Sstevel@tonic-gate void
9087c478bd9Sstevel@tonic-gate altprivsep_packet_read_expect(int expected)
9097c478bd9Sstevel@tonic-gate {
9107c478bd9Sstevel@tonic-gate 	int type;
9117c478bd9Sstevel@tonic-gate 
9127c478bd9Sstevel@tonic-gate 	type = altprivsep_packet_read();
9137c478bd9Sstevel@tonic-gate 
9147c478bd9Sstevel@tonic-gate 	if (type <= 0)
9157c478bd9Sstevel@tonic-gate 		fatal("Monitor not responding");
9167c478bd9Sstevel@tonic-gate 
9177c478bd9Sstevel@tonic-gate 	if (type != expected)
9187c478bd9Sstevel@tonic-gate 		fatal("Protocol error in privilege separation; expected "
9197c478bd9Sstevel@tonic-gate 			"packet type %d, got %d", expected, type);
9207c478bd9Sstevel@tonic-gate }
9217c478bd9Sstevel@tonic-gate 
9227c478bd9Sstevel@tonic-gate static
9237c478bd9Sstevel@tonic-gate u_int
9247c478bd9Sstevel@tonic-gate altprivsep_packet_get_char(void)
9257c478bd9Sstevel@tonic-gate {
9267c478bd9Sstevel@tonic-gate 	return (buffer_get_char(&from_monitor));
9277c478bd9Sstevel@tonic-gate }
9287c478bd9Sstevel@tonic-gate void
9297c478bd9Sstevel@tonic-gate *altprivsep_packet_get_raw(u_int *length_ptr)
9307c478bd9Sstevel@tonic-gate {
9317c478bd9Sstevel@tonic-gate 	if (length_ptr != NULL)
9327c478bd9Sstevel@tonic-gate 		*length_ptr = buffer_len(&from_monitor);
9337c478bd9Sstevel@tonic-gate 
9347c478bd9Sstevel@tonic-gate 	return (buffer_ptr(&from_monitor));
9357c478bd9Sstevel@tonic-gate }
9367c478bd9Sstevel@tonic-gate void
9377c478bd9Sstevel@tonic-gate *altprivsep_packet_get_string(u_int *length_ptr)
9387c478bd9Sstevel@tonic-gate {
9397c478bd9Sstevel@tonic-gate 	return (buffer_get_string(&from_monitor, length_ptr));
9407c478bd9Sstevel@tonic-gate }
941