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*8b0ef7edSZdenek Kotala * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
227c478bd9Sstevel@tonic-gate */
237c478bd9Sstevel@tonic-gate
24cd7d5fafSJan Pechanec #include <fcntl.h>
25cd7d5fafSJan Pechanec #include <sys/types.h>
26cd7d5fafSJan Pechanec #include <sys/types.h>
27cd7d5fafSJan Pechanec #include <sys/stat.h>
28cd7d5fafSJan Pechanec #include <sys/socket.h>
29cd7d5fafSJan Pechanec
30cd7d5fafSJan Pechanec #include <pwd.h>
317c478bd9Sstevel@tonic-gate
327c478bd9Sstevel@tonic-gate #include "includes.h"
337c478bd9Sstevel@tonic-gate #include "atomicio.h"
347c478bd9Sstevel@tonic-gate #include "auth.h"
357c478bd9Sstevel@tonic-gate #include "bufaux.h"
367c478bd9Sstevel@tonic-gate #include "buffer.h"
377c478bd9Sstevel@tonic-gate #include "cipher.h"
387c478bd9Sstevel@tonic-gate #include "compat.h"
397c478bd9Sstevel@tonic-gate #include "dispatch.h"
407c478bd9Sstevel@tonic-gate #include "getput.h"
417c478bd9Sstevel@tonic-gate #include "kex.h"
427c478bd9Sstevel@tonic-gate #include "log.h"
437c478bd9Sstevel@tonic-gate #include "mac.h"
447c478bd9Sstevel@tonic-gate #include "packet.h"
457c478bd9Sstevel@tonic-gate #include "uidswap.h"
467c478bd9Sstevel@tonic-gate #include "ssh2.h"
477c478bd9Sstevel@tonic-gate #include "sshlogin.h"
487c478bd9Sstevel@tonic-gate #include "xmalloc.h"
497c478bd9Sstevel@tonic-gate #include "altprivsep.h"
50cd7d5fafSJan Pechanec #include "canohost.h"
51cd7d5fafSJan Pechanec #include "engine.h"
52cd7d5fafSJan Pechanec #include "servconf.h"
53cd7d5fafSJan Pechanec
54cd7d5fafSJan Pechanec #ifdef HAVE_BSM
55cd7d5fafSJan Pechanec #include "bsmaudit.h"
56cd7d5fafSJan Pechanec adt_session_data_t *ah = NULL;
57cd7d5fafSJan Pechanec #endif /* HAVE_BSM */
58cd7d5fafSJan Pechanec
59cd7d5fafSJan Pechanec #ifdef GSSAPI
60cd7d5fafSJan Pechanec #include "ssh-gss.h"
61cd7d5fafSJan Pechanec extern Gssctxt *xxx_gssctxt;
62cd7d5fafSJan Pechanec #endif /* GSSAPI */
637c478bd9Sstevel@tonic-gate
647c478bd9Sstevel@tonic-gate extern Kex *xxx_kex;
65cd7d5fafSJan Pechanec extern u_char *session_id2;
66cd7d5fafSJan Pechanec extern int session_id2_len;
677c478bd9Sstevel@tonic-gate
687c478bd9Sstevel@tonic-gate static Buffer to_monitor;
697c478bd9Sstevel@tonic-gate static Buffer from_monitor;
707c478bd9Sstevel@tonic-gate
717c478bd9Sstevel@tonic-gate /*
727c478bd9Sstevel@tonic-gate * Sun's Alternative Privilege Separation basics:
737c478bd9Sstevel@tonic-gate *
747c478bd9Sstevel@tonic-gate * Abstract
757c478bd9Sstevel@tonic-gate * --------
767c478bd9Sstevel@tonic-gate *
777c478bd9Sstevel@tonic-gate * sshd(1M) fork()s and drops privs in the child while retaining privs
787c478bd9Sstevel@tonic-gate * in the parent (a.k.a., the monitor). The unprivileged sshd and the
797c478bd9Sstevel@tonic-gate * monitor talk over a pipe using a simple protocol.
807c478bd9Sstevel@tonic-gate *
817c478bd9Sstevel@tonic-gate * The monitor protocol is all about having the monitor carry out the
827c478bd9Sstevel@tonic-gate * only operations that require privileges OR access to privileged
837c478bd9Sstevel@tonic-gate * resources. These are: utmpx/wtmpx record keeping, auditing, and
847c478bd9Sstevel@tonic-gate * SSHv2 re-keying.
857c478bd9Sstevel@tonic-gate *
867c478bd9Sstevel@tonic-gate * Re-Keying
877c478bd9Sstevel@tonic-gate * ---------
887c478bd9Sstevel@tonic-gate *
897c478bd9Sstevel@tonic-gate * Re-keying is the only protocol version specific aspect of sshd in
907c478bd9Sstevel@tonic-gate * which the monitor gets involved.
917c478bd9Sstevel@tonic-gate *
927c478bd9Sstevel@tonic-gate * The monitor processes all SSHv2 re-key protocol packets, but the
937c478bd9Sstevel@tonic-gate * unprivileged sshd process does the transport layer crypto for those
947c478bd9Sstevel@tonic-gate * packets.
957c478bd9Sstevel@tonic-gate *
967c478bd9Sstevel@tonic-gate * The monitor and its unprivileged sshd child process treat
977c478bd9Sstevel@tonic-gate * SSH_MSG_NEWKEYS SSH2 messages specially: a) the monitor does not call
987c478bd9Sstevel@tonic-gate * set_newkeys(), but b) the child asks the monitor for the set of
997c478bd9Sstevel@tonic-gate * negotiated algorithms, key, IV and what not for the relevant
1007c478bd9Sstevel@tonic-gate * transport direction and then calls set_newkeys().
1017c478bd9Sstevel@tonic-gate *
1027c478bd9Sstevel@tonic-gate * Monitor Protocol
1037c478bd9Sstevel@tonic-gate * ----------------
1047c478bd9Sstevel@tonic-gate *
1057c478bd9Sstevel@tonic-gate * Monitor IPC message formats are similar to SSHv2 messages, minus
1067c478bd9Sstevel@tonic-gate * compression, encryption, padding and MACs:
1077c478bd9Sstevel@tonic-gate *
1087c478bd9Sstevel@tonic-gate * - 4 octet message length
1097c478bd9Sstevel@tonic-gate * - message data
1107c478bd9Sstevel@tonic-gate * - 1 octet message type
1117c478bd9Sstevel@tonic-gate * - message data
1127c478bd9Sstevel@tonic-gate *
1137c478bd9Sstevel@tonic-gate * In broad strokes:
1147c478bd9Sstevel@tonic-gate *
1157c478bd9Sstevel@tonic-gate * - IPC: pipe, exit(2)/wait4(2)
1167c478bd9Sstevel@tonic-gate *
1177c478bd9Sstevel@tonic-gate * - threads: the monitor and child are single-threaded
1187c478bd9Sstevel@tonic-gate *
1197c478bd9Sstevel@tonic-gate * - monitor main loop: a variant of server_loop2(), for re-keying only
1207c478bd9Sstevel@tonic-gate * - unpriv child main loop: server_loop2(), as usual
1217c478bd9Sstevel@tonic-gate *
1227c478bd9Sstevel@tonic-gate * - protocol:
1237c478bd9Sstevel@tonic-gate * - key exchange packets are always forwarded as is to the monitor
1247c478bd9Sstevel@tonic-gate * - newkeys, record_login(), record_logout() are special packets
1257c478bd9Sstevel@tonic-gate * using the packet type range reserved for local extensions
1267c478bd9Sstevel@tonic-gate *
1277c478bd9Sstevel@tonic-gate * - the child drops privs and runs like a normal sshd, except that it
1287c478bd9Sstevel@tonic-gate * sets dispatch handlers for key exchange packets that forward the
1297c478bd9Sstevel@tonic-gate * packets to the monitor
1307c478bd9Sstevel@tonic-gate *
1317c478bd9Sstevel@tonic-gate * Event loops:
1327c478bd9Sstevel@tonic-gate *
1337c478bd9Sstevel@tonic-gate * - all monitor protocols are synchronous: because the SSHv2 rekey
1347c478bd9Sstevel@tonic-gate * protocols are synchronous and because the other monitor operations
1357c478bd9Sstevel@tonic-gate * are synchronous (or have no replies),
1367c478bd9Sstevel@tonic-gate *
1377c478bd9Sstevel@tonic-gate * - server_loop2() is modified to check the monitor pipe for rekey
1387c478bd9Sstevel@tonic-gate * packets to forward to the client
1397c478bd9Sstevel@tonic-gate *
1407c478bd9Sstevel@tonic-gate * - and dispatch handlers are set, upon receipt of KEXINIT (and reset
1417c478bd9Sstevel@tonic-gate * when NEWKEYS is sent out) to forward incoming rekey packets to the
1427c478bd9Sstevel@tonic-gate * monitor.
1437c478bd9Sstevel@tonic-gate *
1447c478bd9Sstevel@tonic-gate * - the monitor runs an event loop not unlike server_loop2() and runs
1457c478bd9Sstevel@tonic-gate * key exchanges almost exactly as a pre-altprivsep sshd would
1467c478bd9Sstevel@tonic-gate *
1477c478bd9Sstevel@tonic-gate * - unpriv sshd exit -> monitor cleanup (including audit logout) and exit
1487c478bd9Sstevel@tonic-gate *
1497c478bd9Sstevel@tonic-gate * - fatal() in monitor -> forcibly shutdown() socket and kill/wait for
1507c478bd9Sstevel@tonic-gate * child (so that the audit event for the logout better reflects
1517c478bd9Sstevel@tonic-gate * reality -- i.e., logged out means logged out, but for bg jobs)
1527c478bd9Sstevel@tonic-gate *
1537c478bd9Sstevel@tonic-gate * Message formats:
1547c478bd9Sstevel@tonic-gate *
1557c478bd9Sstevel@tonic-gate * - key exchange packets/replies forwarded "as is"
1567c478bd9Sstevel@tonic-gate *
1577c478bd9Sstevel@tonic-gate * - all other monitor requests are sent as SSH2_PRIV_MSG_ALTPRIVSEP and have a
1587c478bd9Sstevel@tonic-gate * sub-type identifier (one octet)
1597c478bd9Sstevel@tonic-gate * - private request sub-types include:
1607c478bd9Sstevel@tonic-gate * - get new shared secret from last re-key
1617c478bd9Sstevel@tonic-gate * - record login (utmpx/wtmpx), request data contains three arguments:
1627c478bd9Sstevel@tonic-gate * pid, ttyname, program name
1637c478bd9Sstevel@tonic-gate * - record logout (utmpx/wtmpx), request data contains one argument: pid
1647c478bd9Sstevel@tonic-gate *
1657c478bd9Sstevel@tonic-gate * Reply sub-types include:
1667c478bd9Sstevel@tonic-gate *
1677c478bd9Sstevel@tonic-gate * - NOP (for record_login/logout)
1687c478bd9Sstevel@tonic-gate * - new shared secret from last re-key
1697c478bd9Sstevel@tonic-gate */
1707c478bd9Sstevel@tonic-gate
1717c478bd9Sstevel@tonic-gate static int aps_started = 0;
1727c478bd9Sstevel@tonic-gate static int is_monitor = 0;
1737c478bd9Sstevel@tonic-gate
1747c478bd9Sstevel@tonic-gate static pid_t monitor_pid, child_pid;
1757c478bd9Sstevel@tonic-gate static int pipe_fds[2];
1767c478bd9Sstevel@tonic-gate static int pipe_fd = -1;
1777c478bd9Sstevel@tonic-gate static Buffer input_pipe, output_pipe; /* for pipe I/O */
1787c478bd9Sstevel@tonic-gate
1797c478bd9Sstevel@tonic-gate static Authctxt *xxx_authctxt;
1807c478bd9Sstevel@tonic-gate
1817c478bd9Sstevel@tonic-gate /* Monitor functions */
182cd7d5fafSJan Pechanec extern void aps_monitor_loop(Authctxt *authctxt, pid_t child_pid);
1837c478bd9Sstevel@tonic-gate static void aps_record_login(void);
1847c478bd9Sstevel@tonic-gate static void aps_record_logout(void);
185dbe3f931Sjp161948 static void aps_start_rekex(void);
186cd7d5fafSJan Pechanec Authctxt *aps_read_auth_context(void);
187cd7d5fafSJan Pechanec
188cd7d5fafSJan Pechanec /* main functions for handling the monitor */
189cd7d5fafSJan Pechanec static pid_t altprivsep_start_monitor(Authctxt **authctxt);
190cd7d5fafSJan Pechanec static void altprivsep_do_monitor(Authctxt *authctxt, pid_t child_pid);
191cd7d5fafSJan Pechanec static int altprivsep_started(void);
192cd7d5fafSJan Pechanec static int altprivsep_is_monitor(void);
193cd7d5fafSJan Pechanec
194cd7d5fafSJan Pechanec /* calls _to_ monitor from unprivileged process */
195cd7d5fafSJan Pechanec static void altprivsep_get_newkeys(enum kex_modes mode);
196cd7d5fafSJan Pechanec
197cd7d5fafSJan Pechanec /* monitor-side fatal_cleanup callbacks */
198cd7d5fafSJan Pechanec static void altprivsep_shutdown_sock(void *arg);
1997c478bd9Sstevel@tonic-gate
2007c478bd9Sstevel@tonic-gate /* Altprivsep packet utilities for communication with the monitor */
2017c478bd9Sstevel@tonic-gate static void altprivsep_packet_start(u_char);
2027c478bd9Sstevel@tonic-gate static int altprivsep_packet_send(void);
2037c478bd9Sstevel@tonic-gate static int altprivsep_fwd_packet(u_char type);
2047c478bd9Sstevel@tonic-gate
2057c478bd9Sstevel@tonic-gate static int altprivsep_packet_read(void);
2067c478bd9Sstevel@tonic-gate static void altprivsep_packet_read_expect(int type);
2077c478bd9Sstevel@tonic-gate
2087c478bd9Sstevel@tonic-gate static void altprivsep_packet_put_char(int ch);
2097c478bd9Sstevel@tonic-gate static void altprivsep_packet_put_int(u_int value);
2107c478bd9Sstevel@tonic-gate static void altprivsep_packet_put_cstring(const char *str);
2117c478bd9Sstevel@tonic-gate static void altprivsep_packet_put_raw(const void *buf, u_int len);
2127c478bd9Sstevel@tonic-gate
2137c478bd9Sstevel@tonic-gate static u_int altprivsep_packet_get_char(void);
2147c478bd9Sstevel@tonic-gate static void *altprivsep_packet_get_raw(u_int *length_ptr);
2157c478bd9Sstevel@tonic-gate static void *altprivsep_packet_get_string(u_int *length_ptr);
2167c478bd9Sstevel@tonic-gate
217cd7d5fafSJan Pechanec Kex *prepare_for_ssh2_kex(void);
218cd7d5fafSJan Pechanec
2197c478bd9Sstevel@tonic-gate /*
2207c478bd9Sstevel@tonic-gate * Start monitor from privileged sshd process.
2217c478bd9Sstevel@tonic-gate *
2227c478bd9Sstevel@tonic-gate * Return values are like fork(2); the parent is the monitor. The caller should
2237c478bd9Sstevel@tonic-gate * fatal() on error.
2247c478bd9Sstevel@tonic-gate *
225cd7d5fafSJan Pechanec * Note that the monitor waits until the still privileged child finishes the
226cd7d5fafSJan Pechanec * authentication. The child drops its privileges after the authentication.
2277c478bd9Sstevel@tonic-gate */
228cd7d5fafSJan Pechanec static pid_t
altprivsep_start_monitor(Authctxt ** authctxt)229cd7d5fafSJan Pechanec altprivsep_start_monitor(Authctxt **authctxt)
2307c478bd9Sstevel@tonic-gate {
2317c478bd9Sstevel@tonic-gate pid_t pid;
2327c478bd9Sstevel@tonic-gate int junk;
2337c478bd9Sstevel@tonic-gate
234cd7d5fafSJan Pechanec if (aps_started)
2357c478bd9Sstevel@tonic-gate fatal("Monitor startup failed: missing state");
2367c478bd9Sstevel@tonic-gate
2377c478bd9Sstevel@tonic-gate buffer_init(&output_pipe);
2387c478bd9Sstevel@tonic-gate buffer_init(&input_pipe);
2397c478bd9Sstevel@tonic-gate
2407c478bd9Sstevel@tonic-gate if (pipe(pipe_fds) != 0) {
2417c478bd9Sstevel@tonic-gate error("Monitor startup failure: could not create pipes: %s",
2427c478bd9Sstevel@tonic-gate strerror(errno));
2437c478bd9Sstevel@tonic-gate return (-1);
2447c478bd9Sstevel@tonic-gate }
2457c478bd9Sstevel@tonic-gate
2467c478bd9Sstevel@tonic-gate (void) fcntl(pipe_fds[0], F_SETFD, FD_CLOEXEC);
2477c478bd9Sstevel@tonic-gate (void) fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC);
2487c478bd9Sstevel@tonic-gate
2497c478bd9Sstevel@tonic-gate monitor_pid = getpid();
2507c478bd9Sstevel@tonic-gate
2517c478bd9Sstevel@tonic-gate if ((pid = fork()) > 0) {
252cd7d5fafSJan Pechanec /*
253cd7d5fafSJan Pechanec * From now on, all debug messages from monitor will have prefix
254cd7d5fafSJan Pechanec * "monitor "
255cd7d5fafSJan Pechanec */
256cd7d5fafSJan Pechanec set_log_txt_prefix("monitor ");
257cd7d5fafSJan Pechanec (void) prepare_for_ssh2_kex();
258cd7d5fafSJan Pechanec packet_set_server();
2597c478bd9Sstevel@tonic-gate /* parent */
2607c478bd9Sstevel@tonic-gate child_pid = pid;
2617c478bd9Sstevel@tonic-gate
2627c478bd9Sstevel@tonic-gate debug2("Monitor pid %ld, unprivileged child pid %ld",
2637c478bd9Sstevel@tonic-gate monitor_pid, child_pid);
2647c478bd9Sstevel@tonic-gate
2657c478bd9Sstevel@tonic-gate (void) close(pipe_fds[1]);
2667c478bd9Sstevel@tonic-gate pipe_fd = pipe_fds[0];
2677c478bd9Sstevel@tonic-gate
268cd7d5fafSJan Pechanec /*
269cd7d5fafSJan Pechanec * Signal readiness of the monitor and then read the
270cd7d5fafSJan Pechanec * authentication context from the child.
271cd7d5fafSJan Pechanec */
272cd7d5fafSJan Pechanec (void) write(pipe_fd, &pid, sizeof (pid));
273cd7d5fafSJan Pechanec packet_set_monitor(pipe_fd);
274cd7d5fafSJan Pechanec xxx_authctxt = *authctxt = aps_read_auth_context();
275cd7d5fafSJan Pechanec
2767c478bd9Sstevel@tonic-gate if (fcntl(pipe_fd, F_SETFL, O_NONBLOCK) < 0)
2777c478bd9Sstevel@tonic-gate error("fcntl O_NONBLOCK: %.100s", strerror(errno));
2787c478bd9Sstevel@tonic-gate
2797c478bd9Sstevel@tonic-gate aps_started = 1;
2807c478bd9Sstevel@tonic-gate is_monitor = 1;
2817c478bd9Sstevel@tonic-gate
2827c478bd9Sstevel@tonic-gate debug2("Monitor started");
2837c478bd9Sstevel@tonic-gate
2847c478bd9Sstevel@tonic-gate return (pid);
2857c478bd9Sstevel@tonic-gate }
2867c478bd9Sstevel@tonic-gate
2877c478bd9Sstevel@tonic-gate if (pid < 0) {
2887c478bd9Sstevel@tonic-gate debug2("Monitor startup failure: could not fork unprivileged"
2897c478bd9Sstevel@tonic-gate " process: %s", strerror(errno));
2907c478bd9Sstevel@tonic-gate return (pid);
2917c478bd9Sstevel@tonic-gate }
2927c478bd9Sstevel@tonic-gate
293cd7d5fafSJan Pechanec /* this is the child that will later drop privileges */
2947c478bd9Sstevel@tonic-gate
295cd7d5fafSJan Pechanec /* note that Solaris has bi-directional pipes so one pipe is enough */
2967c478bd9Sstevel@tonic-gate (void) close(pipe_fds[0]);
2977c478bd9Sstevel@tonic-gate pipe_fd = pipe_fds[1];
2987c478bd9Sstevel@tonic-gate
2997c478bd9Sstevel@tonic-gate /* wait for monitor to be ready */
3007c478bd9Sstevel@tonic-gate debug2("Waiting for monitor");
3017c478bd9Sstevel@tonic-gate (void) read(pipe_fd, &junk, sizeof (junk));
3027c478bd9Sstevel@tonic-gate debug2("Monitor signalled readiness");
3037c478bd9Sstevel@tonic-gate
3047c478bd9Sstevel@tonic-gate buffer_init(&to_monitor);
3057c478bd9Sstevel@tonic-gate buffer_init(&from_monitor);
3067c478bd9Sstevel@tonic-gate
3077c478bd9Sstevel@tonic-gate /* AltPrivSep interfaces are set up */
3087c478bd9Sstevel@tonic-gate aps_started = 1;
3097c478bd9Sstevel@tonic-gate return (pid);
3107c478bd9Sstevel@tonic-gate }
3117c478bd9Sstevel@tonic-gate
3127c478bd9Sstevel@tonic-gate int
altprivsep_get_pipe_fd(void)3137c478bd9Sstevel@tonic-gate altprivsep_get_pipe_fd(void)
3147c478bd9Sstevel@tonic-gate {
3157c478bd9Sstevel@tonic-gate return (pipe_fd);
3167c478bd9Sstevel@tonic-gate }
3177c478bd9Sstevel@tonic-gate
318cd7d5fafSJan Pechanec /*
319cd7d5fafSJan Pechanec * This function is used in the unprivileged child for all packets in the range
320cd7d5fafSJan Pechanec * between SSH2_MSG_KEXINIT and SSH2_MSG_TRANSPORT_MAX.
321cd7d5fafSJan Pechanec */
3227c478bd9Sstevel@tonic-gate void
altprivsep_rekey(int type,u_int32_t seq,void * ctxt)3237c478bd9Sstevel@tonic-gate altprivsep_rekey(int type, u_int32_t seq, void *ctxt)
3247c478bd9Sstevel@tonic-gate {
3257c478bd9Sstevel@tonic-gate Kex *kex = (Kex *)ctxt;
3267c478bd9Sstevel@tonic-gate
3277c478bd9Sstevel@tonic-gate if (kex == NULL)
3287c478bd9Sstevel@tonic-gate fatal("Missing key exchange context in unprivileged process");
3297c478bd9Sstevel@tonic-gate
330cd7d5fafSJan Pechanec if (type != SSH2_MSG_NEWKEYS) {
3317c478bd9Sstevel@tonic-gate debug2("Forwarding re-key packet (%d) to monitor", type);
3327c478bd9Sstevel@tonic-gate if (!altprivsep_fwd_packet(type))
333*8b0ef7edSZdenek Kotala fatal("altprivsep_rekey: Monitor not responding");
334cd7d5fafSJan Pechanec }
3357c478bd9Sstevel@tonic-gate
3367c478bd9Sstevel@tonic-gate /* tell server_loop2() that we're re-keying */
3377c478bd9Sstevel@tonic-gate kex->done = 0;
3387c478bd9Sstevel@tonic-gate
3397c478bd9Sstevel@tonic-gate /* NEWKEYS is special: get the new keys for client->server direction */
3407c478bd9Sstevel@tonic-gate if (type == SSH2_MSG_NEWKEYS) {
341cd7d5fafSJan Pechanec debug2("received SSH2_MSG_NEWKEYS packet - "
342cd7d5fafSJan Pechanec "getting new inbound keys from the monitor");
3437c478bd9Sstevel@tonic-gate altprivsep_get_newkeys(MODE_IN);
3447c478bd9Sstevel@tonic-gate kex->done = 1;
3457c478bd9Sstevel@tonic-gate }
3467c478bd9Sstevel@tonic-gate }
3477c478bd9Sstevel@tonic-gate
3487c478bd9Sstevel@tonic-gate void
altprivsep_process_input(fd_set * rset)349dbe3f931Sjp161948 altprivsep_process_input(fd_set *rset)
3507c478bd9Sstevel@tonic-gate {
3517c478bd9Sstevel@tonic-gate void *data;
3527c478bd9Sstevel@tonic-gate int type;
3537c478bd9Sstevel@tonic-gate u_int dlen;
3547c478bd9Sstevel@tonic-gate
3557c478bd9Sstevel@tonic-gate if (pipe_fd == -1)
3567c478bd9Sstevel@tonic-gate return;
3577c478bd9Sstevel@tonic-gate
3587c478bd9Sstevel@tonic-gate if (!FD_ISSET(pipe_fd, rset))
3597c478bd9Sstevel@tonic-gate return;
3607c478bd9Sstevel@tonic-gate
361cd7d5fafSJan Pechanec debug2("reading from pipe to monitor (%d)", pipe_fd);
3627c478bd9Sstevel@tonic-gate if ((type = altprivsep_packet_read()) == -1)
363*8b0ef7edSZdenek Kotala fatal("altprivsep_process_input: Monitor not responding");
3647c478bd9Sstevel@tonic-gate
3657c478bd9Sstevel@tonic-gate if (!compat20)
3667c478bd9Sstevel@tonic-gate return; /* shouldn't happen! but be safe */
3677c478bd9Sstevel@tonic-gate
3687c478bd9Sstevel@tonic-gate if (type == 0)
3697c478bd9Sstevel@tonic-gate return; /* EOF -- nothing to do here */
3707c478bd9Sstevel@tonic-gate
3717c478bd9Sstevel@tonic-gate if (type >= SSH2_MSG_MAX)
3727c478bd9Sstevel@tonic-gate fatal("Received garbage from monitor");
3737c478bd9Sstevel@tonic-gate
3747c478bd9Sstevel@tonic-gate debug2("Read packet type %d from pipe to monitor", (u_int)type);
3757c478bd9Sstevel@tonic-gate
3767c478bd9Sstevel@tonic-gate if (type == SSH2_PRIV_MSG_ALTPRIVSEP)
3777c478bd9Sstevel@tonic-gate return; /* shouldn't happen! */
3787c478bd9Sstevel@tonic-gate
3797c478bd9Sstevel@tonic-gate /* NEWKEYS is special: get the new keys for server->client direction */
3807c478bd9Sstevel@tonic-gate if (type == SSH2_MSG_NEWKEYS) {
381cd7d5fafSJan Pechanec debug2("forwarding SSH2_MSG_NEWKEYS packet we got from monitor to "
382cd7d5fafSJan Pechanec "the client");
3837c478bd9Sstevel@tonic-gate packet_start(SSH2_MSG_NEWKEYS);
3847c478bd9Sstevel@tonic-gate packet_send();
385cd7d5fafSJan Pechanec debug2("getting new outbound keys from the monitor");
3867c478bd9Sstevel@tonic-gate altprivsep_get_newkeys(MODE_OUT);
3877c478bd9Sstevel@tonic-gate return;
3887c478bd9Sstevel@tonic-gate }
3897c478bd9Sstevel@tonic-gate
3907c478bd9Sstevel@tonic-gate data = altprivsep_packet_get_raw(&dlen);
3917c478bd9Sstevel@tonic-gate
3927c478bd9Sstevel@tonic-gate packet_start((u_char)type);
3937c478bd9Sstevel@tonic-gate
3947c478bd9Sstevel@tonic-gate if (data != NULL && dlen > 0)
3957c478bd9Sstevel@tonic-gate packet_put_raw(data, dlen);
3967c478bd9Sstevel@tonic-gate
3977c478bd9Sstevel@tonic-gate packet_send();
3987c478bd9Sstevel@tonic-gate }
3997c478bd9Sstevel@tonic-gate
400cd7d5fafSJan Pechanec static void
altprivsep_do_monitor(Authctxt * authctxt,pid_t child_pid)4017c478bd9Sstevel@tonic-gate altprivsep_do_monitor(Authctxt *authctxt, pid_t child_pid)
4027c478bd9Sstevel@tonic-gate {
403cd7d5fafSJan Pechanec aps_monitor_loop(authctxt, child_pid);
4047c478bd9Sstevel@tonic-gate }
4057c478bd9Sstevel@tonic-gate
406cd7d5fafSJan Pechanec static int
altprivsep_started(void)4077c478bd9Sstevel@tonic-gate altprivsep_started(void)
4087c478bd9Sstevel@tonic-gate {
4097c478bd9Sstevel@tonic-gate return (aps_started);
4107c478bd9Sstevel@tonic-gate }
4117c478bd9Sstevel@tonic-gate
412cd7d5fafSJan Pechanec static int
altprivsep_is_monitor(void)4137c478bd9Sstevel@tonic-gate altprivsep_is_monitor(void)
4147c478bd9Sstevel@tonic-gate {
4157c478bd9Sstevel@tonic-gate return (is_monitor);
4167c478bd9Sstevel@tonic-gate }
4177c478bd9Sstevel@tonic-gate
4187c478bd9Sstevel@tonic-gate /*
4197c478bd9Sstevel@tonic-gate * A fatal cleanup function to forcibly shutdown the connection socket
4207c478bd9Sstevel@tonic-gate */
421cd7d5fafSJan Pechanec static void
altprivsep_shutdown_sock(void * arg)4227c478bd9Sstevel@tonic-gate altprivsep_shutdown_sock(void *arg)
4237c478bd9Sstevel@tonic-gate {
4247c478bd9Sstevel@tonic-gate int sock;
4257c478bd9Sstevel@tonic-gate
4267c478bd9Sstevel@tonic-gate if (arg == NULL)
4277c478bd9Sstevel@tonic-gate return;
4287c478bd9Sstevel@tonic-gate
4297c478bd9Sstevel@tonic-gate sock = *(int *)arg;
4307c478bd9Sstevel@tonic-gate
4317c478bd9Sstevel@tonic-gate (void) shutdown(sock, SHUT_RDWR);
4327c478bd9Sstevel@tonic-gate }
4337c478bd9Sstevel@tonic-gate
4347c478bd9Sstevel@tonic-gate /* Calls _to_ monitor from unprivileged process */
435cd7d5fafSJan Pechanec static int
altprivsep_fwd_packet(u_char type)4367c478bd9Sstevel@tonic-gate altprivsep_fwd_packet(u_char type)
4377c478bd9Sstevel@tonic-gate {
4387c478bd9Sstevel@tonic-gate u_int len;
4397c478bd9Sstevel@tonic-gate void *data;
4407c478bd9Sstevel@tonic-gate
4417c478bd9Sstevel@tonic-gate altprivsep_packet_start(type);
4427c478bd9Sstevel@tonic-gate data = packet_get_raw(&len);
4437c478bd9Sstevel@tonic-gate altprivsep_packet_put_raw(data, len);
4447c478bd9Sstevel@tonic-gate
4457c478bd9Sstevel@tonic-gate /* packet_send()s any replies from the monitor to the client */
4467c478bd9Sstevel@tonic-gate return (altprivsep_packet_send());
4477c478bd9Sstevel@tonic-gate }
4487c478bd9Sstevel@tonic-gate
4497c478bd9Sstevel@tonic-gate extern Newkeys *current_keys[MODE_MAX];
4507c478bd9Sstevel@tonic-gate
4517c478bd9Sstevel@tonic-gate /* To be called from packet.c:set_newkeys() before referencing current_keys */
452cd7d5fafSJan Pechanec static void
altprivsep_get_newkeys(enum kex_modes mode)4537c478bd9Sstevel@tonic-gate altprivsep_get_newkeys(enum kex_modes mode)
4547c478bd9Sstevel@tonic-gate {
4557c478bd9Sstevel@tonic-gate Newkeys *newkeys;
4567c478bd9Sstevel@tonic-gate Comp *comp;
4577c478bd9Sstevel@tonic-gate Enc *enc;
4587c478bd9Sstevel@tonic-gate Mac *mac;
4597c478bd9Sstevel@tonic-gate u_int len;
4607c478bd9Sstevel@tonic-gate
4617c478bd9Sstevel@tonic-gate if (!altprivsep_started())
4627c478bd9Sstevel@tonic-gate return;
4637c478bd9Sstevel@tonic-gate
4647c478bd9Sstevel@tonic-gate if (altprivsep_is_monitor())
4657c478bd9Sstevel@tonic-gate return; /* shouldn't happen */
4667c478bd9Sstevel@tonic-gate
4677c478bd9Sstevel@tonic-gate /* request new keys */
4687c478bd9Sstevel@tonic-gate altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
4697c478bd9Sstevel@tonic-gate altprivsep_packet_put_char(APS_MSG_NEWKEYS_REQ);
4707c478bd9Sstevel@tonic-gate altprivsep_packet_put_int((u_int)mode);
4717c478bd9Sstevel@tonic-gate altprivsep_packet_send();
4727c478bd9Sstevel@tonic-gate altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
4737c478bd9Sstevel@tonic-gate if (altprivsep_packet_get_char() != APS_MSG_NEWKEYS_REP)
4747c478bd9Sstevel@tonic-gate fatal("Received garbage from monitor during re-keying");
4757c478bd9Sstevel@tonic-gate
4767c478bd9Sstevel@tonic-gate newkeys = xmalloc(sizeof (*newkeys));
4777c478bd9Sstevel@tonic-gate memset(newkeys, 0, sizeof (*newkeys));
4787c478bd9Sstevel@tonic-gate
4797c478bd9Sstevel@tonic-gate enc = &newkeys->enc;
4807c478bd9Sstevel@tonic-gate mac = &newkeys->mac;
4817c478bd9Sstevel@tonic-gate comp = &newkeys->comp;
4827c478bd9Sstevel@tonic-gate
4837c478bd9Sstevel@tonic-gate /* Cipher name, key, IV */
4847c478bd9Sstevel@tonic-gate enc->name = altprivsep_packet_get_string(NULL);
4857c478bd9Sstevel@tonic-gate if ((enc->cipher = cipher_by_name(enc->name)) == NULL)
4867c478bd9Sstevel@tonic-gate fatal("Monitor negotiated an unknown cipher during re-key");
4877c478bd9Sstevel@tonic-gate
4887c478bd9Sstevel@tonic-gate enc->key = altprivsep_packet_get_string(&enc->key_len);
4897c478bd9Sstevel@tonic-gate enc->iv = altprivsep_packet_get_string(&enc->block_size);
4907c478bd9Sstevel@tonic-gate
4917c478bd9Sstevel@tonic-gate /* MAC name */
4927c478bd9Sstevel@tonic-gate mac->name = altprivsep_packet_get_string(NULL);
4938caf082fSJan Pechanec if (mac_setup(mac, mac->name) < 0)
4947c478bd9Sstevel@tonic-gate fatal("Monitor negotiated an unknown MAC algorithm "
4957c478bd9Sstevel@tonic-gate "during re-key");
4967c478bd9Sstevel@tonic-gate
4977c478bd9Sstevel@tonic-gate mac->key = altprivsep_packet_get_string(&len);
4987c478bd9Sstevel@tonic-gate if (len > mac->key_len)
4997c478bd9Sstevel@tonic-gate fatal("%s: bad mac key length: %d > %d", __func__, len,
5007c478bd9Sstevel@tonic-gate mac->key_len);
5017c478bd9Sstevel@tonic-gate
5027c478bd9Sstevel@tonic-gate /* Compression algorithm name */
5037c478bd9Sstevel@tonic-gate comp->name = altprivsep_packet_get_string(NULL);
5047c478bd9Sstevel@tonic-gate if (strcmp(comp->name, "zlib") != 0 && strcmp(comp->name, "none") != 0)
5057c478bd9Sstevel@tonic-gate fatal("Monitor negotiated an unknown compression "
5067c478bd9Sstevel@tonic-gate "algorithm during re-key");
5077c478bd9Sstevel@tonic-gate
5087c478bd9Sstevel@tonic-gate comp->type = 0;
5097c478bd9Sstevel@tonic-gate comp->enabled = 0; /* forces compression re-init, as per-spec */
5107c478bd9Sstevel@tonic-gate if (strcmp(comp->name, "zlib") == 0)
5117c478bd9Sstevel@tonic-gate comp->type = 1;
5127c478bd9Sstevel@tonic-gate
5137c478bd9Sstevel@tonic-gate /*
5147c478bd9Sstevel@tonic-gate * Now install new keys
5157c478bd9Sstevel@tonic-gate *
5167c478bd9Sstevel@tonic-gate * For now abuse kex.c/packet.c non-interfaces. Someday, when
5177c478bd9Sstevel@tonic-gate * the many internal interfaces are parametrized, made reentrant
5187c478bd9Sstevel@tonic-gate * and thread-safe, made more consistent, and when necessary-but-
5197c478bd9Sstevel@tonic-gate * currently-missing interfaces are added then this bit of
5207c478bd9Sstevel@tonic-gate * ugliness can be revisited.
5217c478bd9Sstevel@tonic-gate *
5227c478bd9Sstevel@tonic-gate * The ugliness is in the set_newkeys(), its name and the lack
5237c478bd9Sstevel@tonic-gate * of a (Newkeys *) parameter, which forces us to pass the
5247c478bd9Sstevel@tonic-gate * newkeys through current_keys[mode]. But this saves us some
5257c478bd9Sstevel@tonic-gate * lines of code for now, though not comments.
5267c478bd9Sstevel@tonic-gate *
5277c478bd9Sstevel@tonic-gate * Also, we've abused, in the code above, knowledge of what
5287c478bd9Sstevel@tonic-gate * set_newkeys() expects the current_keys[mode] to contain.
5297c478bd9Sstevel@tonic-gate */
5307c478bd9Sstevel@tonic-gate current_keys[mode] = newkeys;
5317c478bd9Sstevel@tonic-gate set_newkeys(mode);
5327c478bd9Sstevel@tonic-gate
5337c478bd9Sstevel@tonic-gate }
5347c478bd9Sstevel@tonic-gate
5357c478bd9Sstevel@tonic-gate void
altprivsep_record_login(pid_t pid,const char * ttyname)5367c478bd9Sstevel@tonic-gate altprivsep_record_login(pid_t pid, const char *ttyname)
5377c478bd9Sstevel@tonic-gate {
5387c478bd9Sstevel@tonic-gate altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
5397c478bd9Sstevel@tonic-gate altprivsep_packet_put_char(APS_MSG_RECORD_LOGIN);
5407c478bd9Sstevel@tonic-gate altprivsep_packet_put_int(pid);
5417c478bd9Sstevel@tonic-gate altprivsep_packet_put_cstring(ttyname);
5427c478bd9Sstevel@tonic-gate altprivsep_packet_send();
5437c478bd9Sstevel@tonic-gate altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
5447c478bd9Sstevel@tonic-gate }
5457c478bd9Sstevel@tonic-gate
5467c478bd9Sstevel@tonic-gate void
altprivsep_record_logout(pid_t pid)5477c478bd9Sstevel@tonic-gate altprivsep_record_logout(pid_t pid)
5487c478bd9Sstevel@tonic-gate {
5497c478bd9Sstevel@tonic-gate altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
5507c478bd9Sstevel@tonic-gate altprivsep_packet_put_char(APS_MSG_RECORD_LOGOUT);
5517c478bd9Sstevel@tonic-gate altprivsep_packet_put_int(pid);
5527c478bd9Sstevel@tonic-gate altprivsep_packet_send();
5537c478bd9Sstevel@tonic-gate altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
5547c478bd9Sstevel@tonic-gate }
5557c478bd9Sstevel@tonic-gate
556dbe3f931Sjp161948 void
altprivsep_start_rekex(void)557dbe3f931Sjp161948 altprivsep_start_rekex(void)
558dbe3f931Sjp161948 {
559dbe3f931Sjp161948 altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
560dbe3f931Sjp161948 altprivsep_packet_put_char(APS_MSG_START_REKEX);
561dbe3f931Sjp161948 altprivsep_packet_send();
562dbe3f931Sjp161948 altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
563dbe3f931Sjp161948 }
564dbe3f931Sjp161948
565cd7d5fafSJan Pechanec /*
566cd7d5fafSJan Pechanec * The monitor needs some information that its child learns during the
567cd7d5fafSJan Pechanec * authentication process. Since the child was forked before the key exchange
568cd7d5fafSJan Pechanec * and authentication started it must send some context to the monitor after the
569cd7d5fafSJan Pechanec * authentication is finished. Less obvious part - monitor needs the session ID
570cd7d5fafSJan Pechanec * since it is used in the key generation process after the key (re-)exchange is
571cd7d5fafSJan Pechanec * finished.
572cd7d5fafSJan Pechanec */
573cd7d5fafSJan Pechanec void
altprivsep_send_auth_context(Authctxt * authctxt)574cd7d5fafSJan Pechanec altprivsep_send_auth_context(Authctxt *authctxt)
575cd7d5fafSJan Pechanec {
576cd7d5fafSJan Pechanec debug("sending auth context to the monitor");
577cd7d5fafSJan Pechanec altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
578cd7d5fafSJan Pechanec altprivsep_packet_put_char(APS_MSG_AUTH_CONTEXT);
579cd7d5fafSJan Pechanec altprivsep_packet_put_int(authctxt->pw->pw_uid);
580cd7d5fafSJan Pechanec altprivsep_packet_put_int(authctxt->pw->pw_gid);
581cd7d5fafSJan Pechanec altprivsep_packet_put_cstring(authctxt->pw->pw_name);
582cd7d5fafSJan Pechanec altprivsep_packet_put_raw(session_id2, session_id2_len);
583cd7d5fafSJan Pechanec debug("will send %d bytes of auth context to the monitor",
584cd7d5fafSJan Pechanec buffer_len(&to_monitor));
585cd7d5fafSJan Pechanec altprivsep_packet_send();
586cd7d5fafSJan Pechanec altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
587cd7d5fafSJan Pechanec }
588cd7d5fafSJan Pechanec
5897c478bd9Sstevel@tonic-gate static void aps_send_newkeys(void);
5907c478bd9Sstevel@tonic-gate
5917c478bd9Sstevel@tonic-gate /* Monitor side dispatch handler for SSH2_PRIV_MSG_ALTPRIVSEP */
5927c478bd9Sstevel@tonic-gate /* ARGSUSED */
5937c478bd9Sstevel@tonic-gate void
aps_input_altpriv_msg(int type,u_int32_t seq,void * ctxt)5947c478bd9Sstevel@tonic-gate aps_input_altpriv_msg(int type, u_int32_t seq, void *ctxt)
5957c478bd9Sstevel@tonic-gate {
5967c478bd9Sstevel@tonic-gate u_char req_type;
5977c478bd9Sstevel@tonic-gate
5987c478bd9Sstevel@tonic-gate req_type = packet_get_char();
5997c478bd9Sstevel@tonic-gate
6007c478bd9Sstevel@tonic-gate switch (req_type) {
6017c478bd9Sstevel@tonic-gate case APS_MSG_NEWKEYS_REQ:
6027c478bd9Sstevel@tonic-gate aps_send_newkeys();
6037c478bd9Sstevel@tonic-gate break;
6047c478bd9Sstevel@tonic-gate case APS_MSG_RECORD_LOGIN:
6057c478bd9Sstevel@tonic-gate aps_record_login();
6067c478bd9Sstevel@tonic-gate break;
6077c478bd9Sstevel@tonic-gate case APS_MSG_RECORD_LOGOUT:
6087c478bd9Sstevel@tonic-gate aps_record_logout();
6097c478bd9Sstevel@tonic-gate break;
610dbe3f931Sjp161948 case APS_MSG_START_REKEX:
611dbe3f931Sjp161948 aps_start_rekex();
612dbe3f931Sjp161948 break;
6137c478bd9Sstevel@tonic-gate default:
6147c478bd9Sstevel@tonic-gate break;
6157c478bd9Sstevel@tonic-gate }
6167c478bd9Sstevel@tonic-gate }
6177c478bd9Sstevel@tonic-gate
6187c478bd9Sstevel@tonic-gate /* Monitor-side handlers for APS_MSG_* */
6197c478bd9Sstevel@tonic-gate static
6207c478bd9Sstevel@tonic-gate void
aps_send_newkeys(void)6217c478bd9Sstevel@tonic-gate aps_send_newkeys(void)
6227c478bd9Sstevel@tonic-gate {
6237c478bd9Sstevel@tonic-gate Newkeys *newkeys;
6247c478bd9Sstevel@tonic-gate Enc *enc;
6257c478bd9Sstevel@tonic-gate Mac *mac;
6267c478bd9Sstevel@tonic-gate Comp *comp;
6277c478bd9Sstevel@tonic-gate enum kex_modes mode;
6287c478bd9Sstevel@tonic-gate
6297c478bd9Sstevel@tonic-gate /* get direction for which newkeys are wanted */
6307c478bd9Sstevel@tonic-gate mode = (enum kex_modes) packet_get_int();
6317c478bd9Sstevel@tonic-gate packet_check_eom();
6327c478bd9Sstevel@tonic-gate
6337c478bd9Sstevel@tonic-gate /* get those newkeys */
6347c478bd9Sstevel@tonic-gate newkeys = kex_get_newkeys(mode);
6357c478bd9Sstevel@tonic-gate enc = &newkeys->enc;
6367c478bd9Sstevel@tonic-gate mac = &newkeys->mac;
6377c478bd9Sstevel@tonic-gate comp = &newkeys->comp;
6387c478bd9Sstevel@tonic-gate
6397c478bd9Sstevel@tonic-gate /*
6407c478bd9Sstevel@tonic-gate * Negotiated algorithms, client->server and server->client, for
6417c478bd9Sstevel@tonic-gate * cipher, mac and compression.
6427c478bd9Sstevel@tonic-gate */
6437c478bd9Sstevel@tonic-gate packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
6447c478bd9Sstevel@tonic-gate packet_put_char(APS_MSG_NEWKEYS_REP);
6457c478bd9Sstevel@tonic-gate packet_put_cstring(enc->name);
6467c478bd9Sstevel@tonic-gate packet_put_string(enc->key, enc->key_len);
6477c478bd9Sstevel@tonic-gate packet_put_string(enc->iv, enc->block_size);
6487c478bd9Sstevel@tonic-gate packet_put_cstring(mac->name);
6497c478bd9Sstevel@tonic-gate packet_put_string(mac->key, mac->key_len);
6507c478bd9Sstevel@tonic-gate packet_put_cstring(comp->name);
6517c478bd9Sstevel@tonic-gate
6527c478bd9Sstevel@tonic-gate packet_send();
6539a8058b5Sjp161948 free_keys(newkeys);
6547c478bd9Sstevel@tonic-gate }
6557c478bd9Sstevel@tonic-gate
6567c478bd9Sstevel@tonic-gate struct _aps_login_rec {
6577c478bd9Sstevel@tonic-gate pid_t lr_pid;
6587c478bd9Sstevel@tonic-gate char *lr_tty;
6597c478bd9Sstevel@tonic-gate struct _aps_login_rec *next;
6607c478bd9Sstevel@tonic-gate };
6617c478bd9Sstevel@tonic-gate
6627c478bd9Sstevel@tonic-gate typedef struct _aps_login_rec aps_login_rec;
6637c478bd9Sstevel@tonic-gate
6647c478bd9Sstevel@tonic-gate static aps_login_rec *aps_login_list = NULL;
6657c478bd9Sstevel@tonic-gate
6667c478bd9Sstevel@tonic-gate static
6677c478bd9Sstevel@tonic-gate void
aps_record_login(void)6687c478bd9Sstevel@tonic-gate aps_record_login(void)
6697c478bd9Sstevel@tonic-gate {
6707c478bd9Sstevel@tonic-gate aps_login_rec *new_rec;
6717c478bd9Sstevel@tonic-gate struct stat sbuf;
6727c478bd9Sstevel@tonic-gate size_t proc_path_len;
6737c478bd9Sstevel@tonic-gate char *proc_path;
6747c478bd9Sstevel@tonic-gate
6757c478bd9Sstevel@tonic-gate new_rec = xmalloc(sizeof (aps_login_rec));
6767c478bd9Sstevel@tonic-gate memset(new_rec, 0, sizeof (aps_login_rec));
6777c478bd9Sstevel@tonic-gate
6787c478bd9Sstevel@tonic-gate new_rec->lr_pid = packet_get_int();
6797c478bd9Sstevel@tonic-gate new_rec->lr_tty = packet_get_string(NULL);
6807c478bd9Sstevel@tonic-gate
6817c478bd9Sstevel@tonic-gate proc_path_len = snprintf(NULL, 0, "/proc/%d", new_rec->lr_pid);
6827c478bd9Sstevel@tonic-gate proc_path = xmalloc(proc_path_len + 1);
6837c478bd9Sstevel@tonic-gate (void) snprintf(proc_path, proc_path_len + 1, "/proc/%d",
6847c478bd9Sstevel@tonic-gate new_rec->lr_pid);
6857c478bd9Sstevel@tonic-gate
6867c478bd9Sstevel@tonic-gate if (stat(proc_path, &sbuf) ||
6877c478bd9Sstevel@tonic-gate sbuf.st_uid != xxx_authctxt->pw->pw_uid ||
6887c478bd9Sstevel@tonic-gate stat(new_rec->lr_tty, &sbuf) < 0 ||
6897c478bd9Sstevel@tonic-gate sbuf.st_uid != xxx_authctxt->pw->pw_uid) {
6907c478bd9Sstevel@tonic-gate debug2("Spurious record_login request from unprivileged sshd");
6917c478bd9Sstevel@tonic-gate xfree(proc_path);
6927c478bd9Sstevel@tonic-gate xfree(new_rec->lr_tty);
6937c478bd9Sstevel@tonic-gate xfree(new_rec);
6947c478bd9Sstevel@tonic-gate return;
6957c478bd9Sstevel@tonic-gate }
6967c478bd9Sstevel@tonic-gate
6977c478bd9Sstevel@tonic-gate /* Insert new record on list */
6987c478bd9Sstevel@tonic-gate new_rec->next = aps_login_list;
6997c478bd9Sstevel@tonic-gate aps_login_list = new_rec;
7007c478bd9Sstevel@tonic-gate
7017c478bd9Sstevel@tonic-gate record_login(new_rec->lr_pid, new_rec->lr_tty, NULL,
7027c478bd9Sstevel@tonic-gate xxx_authctxt->user);
7037c478bd9Sstevel@tonic-gate
7047c478bd9Sstevel@tonic-gate packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
7057c478bd9Sstevel@tonic-gate packet_send();
7067c478bd9Sstevel@tonic-gate
7077c478bd9Sstevel@tonic-gate xfree(proc_path);
7087c478bd9Sstevel@tonic-gate }
7097c478bd9Sstevel@tonic-gate
7107c478bd9Sstevel@tonic-gate static
7117c478bd9Sstevel@tonic-gate void
aps_record_logout(void)7127c478bd9Sstevel@tonic-gate aps_record_logout(void)
7137c478bd9Sstevel@tonic-gate {
7147c478bd9Sstevel@tonic-gate aps_login_rec **p, *q;
7157c478bd9Sstevel@tonic-gate pid_t pid;
7167c478bd9Sstevel@tonic-gate
7177c478bd9Sstevel@tonic-gate pid = packet_get_int();
7187c478bd9Sstevel@tonic-gate packet_check_eom();
7197c478bd9Sstevel@tonic-gate
7207c478bd9Sstevel@tonic-gate for (p = &aps_login_list; *p != NULL; p = &q->next) {
7217c478bd9Sstevel@tonic-gate q = *p;
7227c478bd9Sstevel@tonic-gate if (q->lr_pid == pid) {
7237c478bd9Sstevel@tonic-gate record_logout(q->lr_pid, q->lr_tty, NULL,
7247c478bd9Sstevel@tonic-gate xxx_authctxt->user);
7257c478bd9Sstevel@tonic-gate
7267c478bd9Sstevel@tonic-gate /* dequeue */
7277c478bd9Sstevel@tonic-gate *p = q->next;
7287c478bd9Sstevel@tonic-gate xfree(q->lr_tty);
7297c478bd9Sstevel@tonic-gate xfree(q);
7307c478bd9Sstevel@tonic-gate break;
7317c478bd9Sstevel@tonic-gate }
7327c478bd9Sstevel@tonic-gate }
7337c478bd9Sstevel@tonic-gate
7347c478bd9Sstevel@tonic-gate packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
7357c478bd9Sstevel@tonic-gate packet_send();
7367c478bd9Sstevel@tonic-gate }
7377c478bd9Sstevel@tonic-gate
738dbe3f931Sjp161948 static
739dbe3f931Sjp161948 void
aps_start_rekex(void)740dbe3f931Sjp161948 aps_start_rekex(void)
741dbe3f931Sjp161948 {
742dbe3f931Sjp161948 /*
743dbe3f931Sjp161948 * Send confirmation. We could implement it without that but it doesn't
744dbe3f931Sjp161948 * bring any harm to do that and we are consistent with other subtypes
745dbe3f931Sjp161948 * of our private SSH2_PRIV_MSG_ALTPRIVSEP message type.
746dbe3f931Sjp161948 */
747dbe3f931Sjp161948 packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
748dbe3f931Sjp161948 packet_send();
749dbe3f931Sjp161948
750dbe3f931Sjp161948 /*
751dbe3f931Sjp161948 * KEX_INIT message could be the one that reached the limit. In that
752dbe3f931Sjp161948 * case, it was already forwarded to us from the unnprivileged child,
753dbe3f931Sjp161948 * and maybe even acted upon. Obviously we must not send another
754dbe3f931Sjp161948 * KEX_INIT message.
755dbe3f931Sjp161948 */
756dbe3f931Sjp161948 if (!(xxx_kex->flags & KEX_INIT_SENT))
757dbe3f931Sjp161948 kex_send_kexinit(xxx_kex);
758dbe3f931Sjp161948 else
759dbe3f931Sjp161948 debug2("rekeying already in progress");
760dbe3f931Sjp161948 }
761dbe3f931Sjp161948
762cd7d5fafSJan Pechanec /*
763cd7d5fafSJan Pechanec * This is the monitor side of altprivsep_send_auth_context().
764cd7d5fafSJan Pechanec */
765cd7d5fafSJan Pechanec Authctxt *
aps_read_auth_context(void)766cd7d5fafSJan Pechanec aps_read_auth_context(void)
767cd7d5fafSJan Pechanec {
768cd7d5fafSJan Pechanec unsigned char *tmp;
769cd7d5fafSJan Pechanec Authctxt *authctxt;
770cd7d5fafSJan Pechanec
771cd7d5fafSJan Pechanec /*
772cd7d5fafSJan Pechanec * After the successful authentication we get the context. Getting
773cd7d5fafSJan Pechanec * end-of-file means that authentication failed and we can exit as well.
774cd7d5fafSJan Pechanec */
775cd7d5fafSJan Pechanec debug("reading the context from the child");
776cd7d5fafSJan Pechanec packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
777cd7d5fafSJan Pechanec debug3("got SSH2_PRIV_MSG_ALTPRIVSEP");
778cd7d5fafSJan Pechanec if (packet_get_char() != APS_MSG_AUTH_CONTEXT) {
779cd7d5fafSJan Pechanec fatal("APS_MSG_AUTH_CONTEXT message subtype expected.");
780cd7d5fafSJan Pechanec }
781cd7d5fafSJan Pechanec
782cd7d5fafSJan Pechanec authctxt = xcalloc(1, sizeof(Authctxt));
783cd7d5fafSJan Pechanec authctxt->pw = xcalloc(1, sizeof(struct passwd));
784cd7d5fafSJan Pechanec
785cd7d5fafSJan Pechanec /* uid_t and gid_t are integers (UNIX spec) */
786cd7d5fafSJan Pechanec authctxt->pw->pw_uid = packet_get_int();
787cd7d5fafSJan Pechanec authctxt->pw->pw_gid = packet_get_int();
788cd7d5fafSJan Pechanec authctxt->pw->pw_name = packet_get_string(NULL);
789cd7d5fafSJan Pechanec authctxt->user = xstrdup(authctxt->pw->pw_name);
790cd7d5fafSJan Pechanec debug3("uid/gid/username %d/%d/%s", authctxt->pw->pw_uid,
791cd7d5fafSJan Pechanec authctxt->pw->pw_gid, authctxt->user);
792cd7d5fafSJan Pechanec session_id2 = (unsigned char *)packet_get_raw((unsigned int*)&session_id2_len);
793cd7d5fafSJan Pechanec
794cd7d5fafSJan Pechanec /* we don't have this for SSH1. In that case, session_id2_len is 0. */
795cd7d5fafSJan Pechanec if (session_id2_len > 0) {
796cd7d5fafSJan Pechanec tmp = (unsigned char *)xmalloc(session_id2_len);
797cd7d5fafSJan Pechanec memcpy(tmp, session_id2, session_id2_len);
798cd7d5fafSJan Pechanec session_id2 = tmp;
799cd7d5fafSJan Pechanec debug3("read session ID (%d B)", session_id2_len);
800cd7d5fafSJan Pechanec xxx_kex->session_id = tmp;
801cd7d5fafSJan Pechanec xxx_kex->session_id_len = session_id2_len;
802cd7d5fafSJan Pechanec }
803cd7d5fafSJan Pechanec debug("finished reading the context");
804cd7d5fafSJan Pechanec
805cd7d5fafSJan Pechanec /* send confirmation */
806cd7d5fafSJan Pechanec packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
807cd7d5fafSJan Pechanec packet_send();
808cd7d5fafSJan Pechanec
809cd7d5fafSJan Pechanec return (authctxt);
810cd7d5fafSJan Pechanec }
811cd7d5fafSJan Pechanec
812dbe3f931Sjp161948
8137c478bd9Sstevel@tonic-gate /* Utilities for communication with the monitor */
814cd7d5fafSJan Pechanec static void
altprivsep_packet_start(u_char type)8157c478bd9Sstevel@tonic-gate altprivsep_packet_start(u_char type)
8167c478bd9Sstevel@tonic-gate {
8177c478bd9Sstevel@tonic-gate buffer_clear(&to_monitor);
8187c478bd9Sstevel@tonic-gate buffer_put_char(&to_monitor, type);
8197c478bd9Sstevel@tonic-gate }
820cd7d5fafSJan Pechanec
821cd7d5fafSJan Pechanec static void
altprivsep_packet_put_char(int ch)8227c478bd9Sstevel@tonic-gate altprivsep_packet_put_char(int ch)
8237c478bd9Sstevel@tonic-gate {
8247c478bd9Sstevel@tonic-gate buffer_put_char(&to_monitor, ch);
8257c478bd9Sstevel@tonic-gate }
826cd7d5fafSJan Pechanec
827cd7d5fafSJan Pechanec static void
altprivsep_packet_put_int(u_int value)8287c478bd9Sstevel@tonic-gate altprivsep_packet_put_int(u_int value)
8297c478bd9Sstevel@tonic-gate {
8307c478bd9Sstevel@tonic-gate buffer_put_int(&to_monitor, value);
8317c478bd9Sstevel@tonic-gate }
832cd7d5fafSJan Pechanec
833cd7d5fafSJan Pechanec static void
altprivsep_packet_put_cstring(const char * str)8347c478bd9Sstevel@tonic-gate altprivsep_packet_put_cstring(const char *str)
8357c478bd9Sstevel@tonic-gate {
8367c478bd9Sstevel@tonic-gate buffer_put_cstring(&to_monitor, str);
8377c478bd9Sstevel@tonic-gate }
838cd7d5fafSJan Pechanec
839cd7d5fafSJan Pechanec static void
altprivsep_packet_put_raw(const void * buf,u_int len)8407c478bd9Sstevel@tonic-gate altprivsep_packet_put_raw(const void *buf, u_int len)
8417c478bd9Sstevel@tonic-gate {
8427c478bd9Sstevel@tonic-gate buffer_append(&to_monitor, buf, len);
8437c478bd9Sstevel@tonic-gate }
8447c478bd9Sstevel@tonic-gate
8457c478bd9Sstevel@tonic-gate /*
8467c478bd9Sstevel@tonic-gate * Send a monitor packet to the monitor. This function is blocking.
8477c478bd9Sstevel@tonic-gate *
8487c478bd9Sstevel@tonic-gate * Returns -1 if the monitor pipe has been closed earlier, fatal()s if
8497c478bd9Sstevel@tonic-gate * there's any other problems.
8507c478bd9Sstevel@tonic-gate */
851cd7d5fafSJan Pechanec static int
altprivsep_packet_send(void)8527c478bd9Sstevel@tonic-gate altprivsep_packet_send(void)
8537c478bd9Sstevel@tonic-gate {
8547c478bd9Sstevel@tonic-gate ssize_t len;
8557c478bd9Sstevel@tonic-gate u_int32_t plen; /* packet length */
8567c478bd9Sstevel@tonic-gate u_char plen_buf[sizeof (plen)];
8577c478bd9Sstevel@tonic-gate u_char padlen; /* padding length */
8587c478bd9Sstevel@tonic-gate fd_set *setp;
859*8b0ef7edSZdenek Kotala int err;
8607c478bd9Sstevel@tonic-gate
8617c478bd9Sstevel@tonic-gate if (pipe_fd == -1)
8627c478bd9Sstevel@tonic-gate return (-1);
8637c478bd9Sstevel@tonic-gate
8647c478bd9Sstevel@tonic-gate if ((plen = buffer_len(&to_monitor)) == 0)
8657c478bd9Sstevel@tonic-gate return (0);
8667c478bd9Sstevel@tonic-gate
8677c478bd9Sstevel@tonic-gate /*
8687c478bd9Sstevel@tonic-gate * We talk the SSHv2 binary packet protocol to the monitor,
8697c478bd9Sstevel@tonic-gate * using the none cipher, mac and compression algorithms.
8707c478bd9Sstevel@tonic-gate *
8717c478bd9Sstevel@tonic-gate * But, interestingly, the none cipher has a block size of 8
8727c478bd9Sstevel@tonic-gate * bytes, thus we must pad the packet.
8737c478bd9Sstevel@tonic-gate *
8747c478bd9Sstevel@tonic-gate * Also, encryption includes the packet length, so the padding
8757c478bd9Sstevel@tonic-gate * must account for that field. I.e., (sizeof (packet length) +
8767c478bd9Sstevel@tonic-gate * sizeof (padding length) + packet length + padding length) %
8777c478bd9Sstevel@tonic-gate * block_size must == 0.
8787c478bd9Sstevel@tonic-gate *
8797c478bd9Sstevel@tonic-gate * Also, there must be at least four (4) bytes of padding.
8807c478bd9Sstevel@tonic-gate */
8817c478bd9Sstevel@tonic-gate padlen = (8 - ((plen + sizeof (plen) + sizeof (padlen)) % 8)) % 8;
8827c478bd9Sstevel@tonic-gate if (padlen < 4)
8837c478bd9Sstevel@tonic-gate padlen += 8;
8847c478bd9Sstevel@tonic-gate
8857c478bd9Sstevel@tonic-gate /* packet length counts padding and padding length field */
8867c478bd9Sstevel@tonic-gate plen += padlen + sizeof (padlen);
8877c478bd9Sstevel@tonic-gate
8887c478bd9Sstevel@tonic-gate PUT_32BIT(plen_buf, plen);
8897c478bd9Sstevel@tonic-gate
8907c478bd9Sstevel@tonic-gate setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
8917c478bd9Sstevel@tonic-gate memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
8927c478bd9Sstevel@tonic-gate FD_SET(pipe_fd, setp);
8937c478bd9Sstevel@tonic-gate
8947c478bd9Sstevel@tonic-gate while (select(pipe_fd + 1, NULL, setp, NULL, NULL) == -1) {
8957c478bd9Sstevel@tonic-gate if (errno == EAGAIN || errno == EINTR)
8967c478bd9Sstevel@tonic-gate continue;
8977c478bd9Sstevel@tonic-gate else
8987c478bd9Sstevel@tonic-gate goto pipe_gone;
8997c478bd9Sstevel@tonic-gate }
9007c478bd9Sstevel@tonic-gate
9017c478bd9Sstevel@tonic-gate xfree(setp);
9027c478bd9Sstevel@tonic-gate
9037c478bd9Sstevel@tonic-gate /* packet length field */
9047c478bd9Sstevel@tonic-gate len = atomicio(write, pipe_fd, plen_buf, sizeof (plen));
9057c478bd9Sstevel@tonic-gate
9067c478bd9Sstevel@tonic-gate if (len != sizeof (plen))
9077c478bd9Sstevel@tonic-gate goto pipe_gone;
9087c478bd9Sstevel@tonic-gate
9097c478bd9Sstevel@tonic-gate /* padding length field */
9107c478bd9Sstevel@tonic-gate len = atomicio(write, pipe_fd, &padlen, sizeof (padlen));
9117c478bd9Sstevel@tonic-gate
9127c478bd9Sstevel@tonic-gate if (len != sizeof (padlen))
9137c478bd9Sstevel@tonic-gate goto pipe_gone;
9147c478bd9Sstevel@tonic-gate
9157c478bd9Sstevel@tonic-gate len = atomicio(write, pipe_fd, buffer_ptr(&to_monitor), plen - 1);
9167c478bd9Sstevel@tonic-gate
9177c478bd9Sstevel@tonic-gate if (len != (plen - 1))
9187c478bd9Sstevel@tonic-gate goto pipe_gone;
9197c478bd9Sstevel@tonic-gate
9207c478bd9Sstevel@tonic-gate buffer_clear(&to_monitor);
9217c478bd9Sstevel@tonic-gate
9227c478bd9Sstevel@tonic-gate return (1);
9237c478bd9Sstevel@tonic-gate
9247c478bd9Sstevel@tonic-gate pipe_gone:
9257c478bd9Sstevel@tonic-gate
926*8b0ef7edSZdenek Kotala err = errno;
927*8b0ef7edSZdenek Kotala
9287c478bd9Sstevel@tonic-gate (void) close(pipe_fd);
9297c478bd9Sstevel@tonic-gate
9307c478bd9Sstevel@tonic-gate pipe_fd = -1;
9317c478bd9Sstevel@tonic-gate
932*8b0ef7edSZdenek Kotala fatal("altprvsep_packet_send: Monitor not responding: %.100s",
933*8b0ef7edSZdenek Kotala strerror(err));
9347c478bd9Sstevel@tonic-gate
9357c478bd9Sstevel@tonic-gate /* NOTREACHED */
9367c478bd9Sstevel@tonic-gate return (0);
9377c478bd9Sstevel@tonic-gate }
9387c478bd9Sstevel@tonic-gate
9397c478bd9Sstevel@tonic-gate /*
9407c478bd9Sstevel@tonic-gate * Read a monitor packet from the monitor. This function is blocking.
9417c478bd9Sstevel@tonic-gate */
942cd7d5fafSJan Pechanec static int
altprivsep_packet_read(void)9437c478bd9Sstevel@tonic-gate altprivsep_packet_read(void)
9447c478bd9Sstevel@tonic-gate {
9457c478bd9Sstevel@tonic-gate ssize_t len = -1;
9467c478bd9Sstevel@tonic-gate u_int32_t plen;
9477c478bd9Sstevel@tonic-gate u_char plen_buf[sizeof (plen)];
9487c478bd9Sstevel@tonic-gate u_char padlen;
9497c478bd9Sstevel@tonic-gate fd_set *setp;
950*8b0ef7edSZdenek Kotala int err;
9517c478bd9Sstevel@tonic-gate
9527c478bd9Sstevel@tonic-gate if (pipe_fd == -1)
9537c478bd9Sstevel@tonic-gate return (-1);
9547c478bd9Sstevel@tonic-gate
9557c478bd9Sstevel@tonic-gate setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
9567c478bd9Sstevel@tonic-gate memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
9577c478bd9Sstevel@tonic-gate FD_SET(pipe_fd, setp);
9587c478bd9Sstevel@tonic-gate
9597c478bd9Sstevel@tonic-gate while (select(pipe_fd + 1, setp, NULL, NULL, NULL) == -1) {
9607c478bd9Sstevel@tonic-gate if (errno == EAGAIN || errno == EINTR)
9617c478bd9Sstevel@tonic-gate continue;
9627c478bd9Sstevel@tonic-gate else
9637c478bd9Sstevel@tonic-gate goto pipe_gone;
9647c478bd9Sstevel@tonic-gate }
9657c478bd9Sstevel@tonic-gate
9667c478bd9Sstevel@tonic-gate xfree(setp);
9677c478bd9Sstevel@tonic-gate
9687c478bd9Sstevel@tonic-gate /* packet length field */
9697c478bd9Sstevel@tonic-gate len = atomicio(read, pipe_fd, plen_buf, sizeof (plen));
9707c478bd9Sstevel@tonic-gate
9717c478bd9Sstevel@tonic-gate plen = GET_32BIT(plen_buf);
9727c478bd9Sstevel@tonic-gate
9737c478bd9Sstevel@tonic-gate if (len != sizeof (plen))
9747c478bd9Sstevel@tonic-gate goto pipe_gone;
9757c478bd9Sstevel@tonic-gate
9767c478bd9Sstevel@tonic-gate /* padding length field */
9777c478bd9Sstevel@tonic-gate len = atomicio(read, pipe_fd, &padlen, sizeof (padlen));
9787c478bd9Sstevel@tonic-gate
9797c478bd9Sstevel@tonic-gate if (len != sizeof (padlen))
9807c478bd9Sstevel@tonic-gate goto pipe_gone;
9817c478bd9Sstevel@tonic-gate
9827c478bd9Sstevel@tonic-gate plen -= sizeof (padlen);
9837c478bd9Sstevel@tonic-gate
9847c478bd9Sstevel@tonic-gate buffer_clear(&from_monitor);
9857c478bd9Sstevel@tonic-gate buffer_append_space(&from_monitor, plen);
9867c478bd9Sstevel@tonic-gate
9877c478bd9Sstevel@tonic-gate /* packet data + padding */
9887c478bd9Sstevel@tonic-gate len = atomicio(read, pipe_fd, buffer_ptr(&from_monitor), plen);
9897c478bd9Sstevel@tonic-gate
9907c478bd9Sstevel@tonic-gate if (len != plen)
9917c478bd9Sstevel@tonic-gate goto pipe_gone;
9927c478bd9Sstevel@tonic-gate
9937c478bd9Sstevel@tonic-gate /* remove padding */
9947c478bd9Sstevel@tonic-gate if (padlen > 0)
9957c478bd9Sstevel@tonic-gate buffer_consume_end(&from_monitor, padlen);
9967c478bd9Sstevel@tonic-gate
9977c478bd9Sstevel@tonic-gate /* packet type */
9987c478bd9Sstevel@tonic-gate return (buffer_get_char(&from_monitor));
9997c478bd9Sstevel@tonic-gate
10007c478bd9Sstevel@tonic-gate pipe_gone:
10017c478bd9Sstevel@tonic-gate
1002*8b0ef7edSZdenek Kotala err = errno;
1003*8b0ef7edSZdenek Kotala
10047c478bd9Sstevel@tonic-gate (void) close(pipe_fd);
10057c478bd9Sstevel@tonic-gate
10067c478bd9Sstevel@tonic-gate pipe_fd = -1;
10077c478bd9Sstevel@tonic-gate
10087c478bd9Sstevel@tonic-gate if (len < 0)
1009*8b0ef7edSZdenek Kotala fatal("altpriv_packet_read: Monitor not responding %.100s",
1010*8b0ef7edSZdenek Kotala strerror(err));
10117c478bd9Sstevel@tonic-gate
10127c478bd9Sstevel@tonic-gate debug2("Monitor pipe closed by monitor");
10137c478bd9Sstevel@tonic-gate return (0);
10147c478bd9Sstevel@tonic-gate }
10157c478bd9Sstevel@tonic-gate
1016cd7d5fafSJan Pechanec static void
altprivsep_packet_read_expect(int expected)10177c478bd9Sstevel@tonic-gate altprivsep_packet_read_expect(int expected)
10187c478bd9Sstevel@tonic-gate {
10197c478bd9Sstevel@tonic-gate int type;
10207c478bd9Sstevel@tonic-gate
10217c478bd9Sstevel@tonic-gate type = altprivsep_packet_read();
10227c478bd9Sstevel@tonic-gate
10237c478bd9Sstevel@tonic-gate if (type <= 0)
1024*8b0ef7edSZdenek Kotala fatal("altprivsep_packet_read_expect: Monitor not responding");
10257c478bd9Sstevel@tonic-gate
10267c478bd9Sstevel@tonic-gate if (type != expected)
10277c478bd9Sstevel@tonic-gate fatal("Protocol error in privilege separation; expected "
10287c478bd9Sstevel@tonic-gate "packet type %d, got %d", expected, type);
10297c478bd9Sstevel@tonic-gate }
10307c478bd9Sstevel@tonic-gate
1031cd7d5fafSJan Pechanec static u_int
altprivsep_packet_get_char(void)10327c478bd9Sstevel@tonic-gate altprivsep_packet_get_char(void)
10337c478bd9Sstevel@tonic-gate {
10347c478bd9Sstevel@tonic-gate return (buffer_get_char(&from_monitor));
10357c478bd9Sstevel@tonic-gate }
10367c478bd9Sstevel@tonic-gate void
altprivsep_packet_get_raw(u_int * length_ptr)10377c478bd9Sstevel@tonic-gate *altprivsep_packet_get_raw(u_int *length_ptr)
10387c478bd9Sstevel@tonic-gate {
10397c478bd9Sstevel@tonic-gate if (length_ptr != NULL)
10407c478bd9Sstevel@tonic-gate *length_ptr = buffer_len(&from_monitor);
10417c478bd9Sstevel@tonic-gate
10427c478bd9Sstevel@tonic-gate return (buffer_ptr(&from_monitor));
10437c478bd9Sstevel@tonic-gate }
10447c478bd9Sstevel@tonic-gate void
altprivsep_packet_get_string(u_int * length_ptr)10457c478bd9Sstevel@tonic-gate *altprivsep_packet_get_string(u_int *length_ptr)
10467c478bd9Sstevel@tonic-gate {
10477c478bd9Sstevel@tonic-gate return (buffer_get_string(&from_monitor, length_ptr));
10487c478bd9Sstevel@tonic-gate }
1049cd7d5fafSJan Pechanec
10503a7bd039SJan Pechanec /*
10513a7bd039SJan Pechanec * Start and execute the code for the monitor which never returns from this
10523a7bd039SJan Pechanec * function. The child will return and continue in the caller.
10533a7bd039SJan Pechanec */
1054cd7d5fafSJan Pechanec void
altprivsep_start_and_do_monitor(int use_engine,int inetd,int newsock,int statup_pipe)1055cd7d5fafSJan Pechanec altprivsep_start_and_do_monitor(int use_engine, int inetd, int newsock,
1056cd7d5fafSJan Pechanec int statup_pipe)
1057cd7d5fafSJan Pechanec {
1058cd7d5fafSJan Pechanec pid_t aps_child;
1059cd7d5fafSJan Pechanec Authctxt *authctxt;
1060cd7d5fafSJan Pechanec
1061cd7d5fafSJan Pechanec /*
1062cd7d5fafSJan Pechanec * The monitor will packet_close() in packet_set_monitor() called from
1063cd7d5fafSJan Pechanec * altprivsep_start_monitor() below to clean up the socket stuff before
1064cd7d5fafSJan Pechanec * it switches to pipes for communication to the child. The socket fd is
1065cd7d5fafSJan Pechanec * closed there so we must dup it here - monitor needs that socket to
1066cd7d5fafSJan Pechanec * shutdown the connection in case of any problem; see comments below.
1067cd7d5fafSJan Pechanec * Note that current newsock was assigned to connection_(in|out) which
1068cd7d5fafSJan Pechanec * are the variables used in packet_close() to close the communication
1069cd7d5fafSJan Pechanec * socket.
1070cd7d5fafSJan Pechanec */
1071cd7d5fafSJan Pechanec newsock = dup(newsock);
1072cd7d5fafSJan Pechanec
1073cd7d5fafSJan Pechanec if ((aps_child = altprivsep_start_monitor(&authctxt)) == -1)
1074cd7d5fafSJan Pechanec fatal("Monitor could not be started.");
1075cd7d5fafSJan Pechanec
1076cd7d5fafSJan Pechanec if (aps_child > 0) {
1077cd7d5fafSJan Pechanec /* ALTPRIVSEP Monitor */
1078cd7d5fafSJan Pechanec
1079cd7d5fafSJan Pechanec /*
1080cd7d5fafSJan Pechanec * The ALTPRIVSEP monitor here does:
1081cd7d5fafSJan Pechanec *
1082cd7d5fafSJan Pechanec * - record keeping and auditing
1083cd7d5fafSJan Pechanec * - PAM cleanup
1084cd7d5fafSJan Pechanec */
1085cd7d5fafSJan Pechanec
1086cd7d5fafSJan Pechanec /* this is for MaxStartups and the child takes care of that */
1087cd7d5fafSJan Pechanec (void) close(statup_pipe);
1088cd7d5fafSJan Pechanec (void) pkcs11_engine_load(use_engine);
1089cd7d5fafSJan Pechanec
1090cd7d5fafSJan Pechanec /*
1091cd7d5fafSJan Pechanec * If the monitor fatal()s it will audit/record a logout, so
1092cd7d5fafSJan Pechanec * we'd better do something to really mean it: shutdown the
1093cd7d5fafSJan Pechanec * socket but leave the child alone -- it's been disconnected
1094cd7d5fafSJan Pechanec * and we hope it exits, but killing any pid from a privileged
1095cd7d5fafSJan Pechanec * monitor could be dangerous.
1096cd7d5fafSJan Pechanec *
1097cd7d5fafSJan Pechanec * NOTE: Order matters -- these fatal cleanups must come before
1098cd7d5fafSJan Pechanec * the audit logout fatal cleanup as these functions are called
1099cd7d5fafSJan Pechanec * in LIFO.
1100cd7d5fafSJan Pechanec */
1101cd7d5fafSJan Pechanec fatal_add_cleanup((void (*)(void *))altprivsep_shutdown_sock,
1102cd7d5fafSJan Pechanec (void *)&newsock);
1103cd7d5fafSJan Pechanec
1104cd7d5fafSJan Pechanec if (compat20) {
1105cd7d5fafSJan Pechanec debug3("Recording SSHv2 session login in wtmpx");
1106cd7d5fafSJan Pechanec /*
1107cd7d5fafSJan Pechanec * record_login() relies on connection_in to be the
1108cd7d5fafSJan Pechanec * socket to get the peer address. The problem is that
1109cd7d5fafSJan Pechanec * connection_in had to be set to the pipe descriptor in
1110cd7d5fafSJan Pechanec * altprivsep_start_monitor(). It's not nice but the
1111cd7d5fafSJan Pechanec * easiest way to get the peer's address is to
1112cd7d5fafSJan Pechanec * temporarily set connection_in to the socket's file
1113cd7d5fafSJan Pechanec * descriptor.
1114cd7d5fafSJan Pechanec */
1115cd7d5fafSJan Pechanec packet_set_fds(inetd == 1 ? -1 : newsock, 0);
1116cd7d5fafSJan Pechanec record_login(getpid(), NULL, "sshd", authctxt->user);
1117cd7d5fafSJan Pechanec packet_set_fds(0, 1);
1118cd7d5fafSJan Pechanec }
1119cd7d5fafSJan Pechanec
1120cd7d5fafSJan Pechanec #ifdef HAVE_BSM
1121cd7d5fafSJan Pechanec /* Initialize the group list, audit sometimes needs it. */
1122cd7d5fafSJan Pechanec if (initgroups(authctxt->pw->pw_name,
1123cd7d5fafSJan Pechanec authctxt->pw->pw_gid) < 0) {
1124cd7d5fafSJan Pechanec perror("initgroups");
1125cd7d5fafSJan Pechanec exit (1);
1126cd7d5fafSJan Pechanec }
1127cd7d5fafSJan Pechanec
1128bca3984eSBrent Paulson /*
1129bca3984eSBrent Paulson * The monitor process fork()ed before the authentication
1130bca3984eSBrent Paulson * process started so at this point we have an unaudited
1131bca3984eSBrent Paulson * context. Thus we need to obtain the audit session data
1132bca3984eSBrent Paulson * from the authentication process (aps_child) which will
1133bca3984eSBrent Paulson * have the correct audit context for the user logging in.
1134bca3984eSBrent Paulson * To do so we pass along the process-ID of the aps_child
1135bca3984eSBrent Paulson * process so that it is referenced for this audit session
1136bca3984eSBrent Paulson * rather than referencing the monitor's unaudited context.
1137bca3984eSBrent Paulson */
1138bca3984eSBrent Paulson audit_sshd_login(&ah, aps_child);
1139cd7d5fafSJan Pechanec
1140cd7d5fafSJan Pechanec fatal_add_cleanup((void (*)(void *))audit_sshd_logout,
1141cd7d5fafSJan Pechanec (void *)&ah);
1142cd7d5fafSJan Pechanec #endif /* HAVE_BSM */
1143cd7d5fafSJan Pechanec
1144cd7d5fafSJan Pechanec #ifdef GSSAPI
1145cd7d5fafSJan Pechanec fatal_add_cleanup((void (*)(void *))ssh_gssapi_cleanup_creds,
1146cd7d5fafSJan Pechanec (void *)&xxx_gssctxt);
1147cd7d5fafSJan Pechanec #endif /* GSSAPI */
1148cd7d5fafSJan Pechanec
1149cd7d5fafSJan Pechanec altprivsep_do_monitor(authctxt, aps_child);
1150cd7d5fafSJan Pechanec
1151cd7d5fafSJan Pechanec /* If we got here the connection is dead. */
1152cd7d5fafSJan Pechanec fatal_remove_cleanup((void (*)(void *))altprivsep_shutdown_sock,
1153cd7d5fafSJan Pechanec (void *)&newsock);
1154cd7d5fafSJan Pechanec
1155cd7d5fafSJan Pechanec if (compat20) {
1156cd7d5fafSJan Pechanec debug3("Recording SSHv2 session logout in wtmpx");
1157cd7d5fafSJan Pechanec record_logout(getpid(), NULL, "sshd", authctxt->user);
1158cd7d5fafSJan Pechanec }
1159cd7d5fafSJan Pechanec
1160cd7d5fafSJan Pechanec /*
1161cd7d5fafSJan Pechanec * Make sure the socket is closed. The monitor can't call
1162cd7d5fafSJan Pechanec * packet_close here as it's done a packet_set_connection()
1163cd7d5fafSJan Pechanec * with the pipe to the child instead of the socket.
1164cd7d5fafSJan Pechanec */
1165cd7d5fafSJan Pechanec (void) shutdown(newsock, SHUT_RDWR);
1166cd7d5fafSJan Pechanec
1167cd7d5fafSJan Pechanec #ifdef GSSAPI
1168cd7d5fafSJan Pechanec fatal_remove_cleanup((void (*)(void *))ssh_gssapi_cleanup_creds,
1169cd7d5fafSJan Pechanec &xxx_gssctxt);
1170cd7d5fafSJan Pechanec ssh_gssapi_cleanup_creds(xxx_gssctxt);
1171cd7d5fafSJan Pechanec ssh_gssapi_server_mechs(NULL); /* release cached mechs list */
1172cd7d5fafSJan Pechanec #endif /* GSSAPI */
1173cd7d5fafSJan Pechanec
1174cd7d5fafSJan Pechanec #ifdef HAVE_BSM
1175cd7d5fafSJan Pechanec fatal_remove_cleanup((void (*)(void *))audit_sshd_logout, (void *)&ah);
1176cd7d5fafSJan Pechanec audit_sshd_logout(&ah);
1177cd7d5fafSJan Pechanec #endif /* HAVE_BSM */
1178cd7d5fafSJan Pechanec
1179cd7d5fafSJan Pechanec exit(0);
1180cd7d5fafSJan Pechanec } else {
1181cd7d5fafSJan Pechanec /*
1182cd7d5fafSJan Pechanec * This is the child, close the dup()ed file descriptor for a
1183cd7d5fafSJan Pechanec * socket. It's not needed in the child.
1184cd7d5fafSJan Pechanec */
1185cd7d5fafSJan Pechanec close(newsock);
1186cd7d5fafSJan Pechanec }
1187cd7d5fafSJan Pechanec }
1188