summaryrefslogtreecommitdiff
path: root/usr/src/cmd/ssh/sshd/altprivsep.c
diff options
context:
space:
mode:
Diffstat (limited to 'usr/src/cmd/ssh/sshd/altprivsep.c')
-rw-r--r--usr/src/cmd/ssh/sshd/altprivsep.c1187
1 files changed, 0 insertions, 1187 deletions
diff --git a/usr/src/cmd/ssh/sshd/altprivsep.c b/usr/src/cmd/ssh/sshd/altprivsep.c
deleted file mode 100644
index 6f954feab5..0000000000
--- a/usr/src/cmd/ssh/sshd/altprivsep.c
+++ /dev/null
@@ -1,1187 +0,0 @@
-/*
- * CDDL HEADER START
- *
- * The contents of this file are subject to the terms of the
- * Common Development and Distribution License (the "License").
- * You may not use this file except in compliance with the License.
- *
- * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
- * or http://www.opensolaris.org/os/licensing.
- * See the License for the specific language governing permissions
- * and limitations under the License.
- *
- * When distributing Covered Code, include this CDDL HEADER in each
- * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
- * If applicable, add the following below this CDDL HEADER, with the
- * fields enclosed by brackets "[]" replaced with your own identifying
- * information: Portions Copyright [yyyy] [name of copyright owner]
- *
- * CDDL HEADER END
- *
- * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
- */
-
-#include <fcntl.h>
-#include <sys/types.h>
-#include <sys/types.h>
-#include <sys/stat.h>
-#include <sys/socket.h>
-
-#include <pwd.h>
-
-#include "includes.h"
-#include "atomicio.h"
-#include "auth.h"
-#include "bufaux.h"
-#include "buffer.h"
-#include "cipher.h"
-#include "compat.h"
-#include "dispatch.h"
-#include "getput.h"
-#include "kex.h"
-#include "log.h"
-#include "mac.h"
-#include "packet.h"
-#include "uidswap.h"
-#include "ssh2.h"
-#include "sshlogin.h"
-#include "xmalloc.h"
-#include "altprivsep.h"
-#include "canohost.h"
-#include "engine.h"
-#include "servconf.h"
-
-#ifdef HAVE_BSM
-#include "bsmaudit.h"
-adt_session_data_t *ah = NULL;
-#endif /* HAVE_BSM */
-
-#ifdef GSSAPI
-#include "ssh-gss.h"
-extern Gssctxt *xxx_gssctxt;
-#endif /* GSSAPI */
-
-extern Kex *xxx_kex;
-extern u_char *session_id2;
-extern int session_id2_len;
-
-static Buffer to_monitor;
-static Buffer from_monitor;
-
-/*
- * Sun's Alternative Privilege Separation basics:
- *
- * Abstract
- * --------
- *
- * sshd(1M) fork()s and drops privs in the child while retaining privs
- * in the parent (a.k.a., the monitor). The unprivileged sshd and the
- * monitor talk over a pipe using a simple protocol.
- *
- * The monitor protocol is all about having the monitor carry out the
- * only operations that require privileges OR access to privileged
- * resources. These are: utmpx/wtmpx record keeping, auditing, and
- * SSHv2 re-keying.
- *
- * Re-Keying
- * ---------
- *
- * Re-keying is the only protocol version specific aspect of sshd in
- * which the monitor gets involved.
- *
- * The monitor processes all SSHv2 re-key protocol packets, but the
- * unprivileged sshd process does the transport layer crypto for those
- * packets.
- *
- * The monitor and its unprivileged sshd child process treat
- * SSH_MSG_NEWKEYS SSH2 messages specially: a) the monitor does not call
- * set_newkeys(), but b) the child asks the monitor for the set of
- * negotiated algorithms, key, IV and what not for the relevant
- * transport direction and then calls set_newkeys().
- *
- * Monitor Protocol
- * ----------------
- *
- * Monitor IPC message formats are similar to SSHv2 messages, minus
- * compression, encryption, padding and MACs:
- *
- * - 4 octet message length
- * - message data
- * - 1 octet message type
- * - message data
- *
- * In broad strokes:
- *
- * - IPC: pipe, exit(2)/wait4(2)
- *
- * - threads: the monitor and child are single-threaded
- *
- * - monitor main loop: a variant of server_loop2(), for re-keying only
- * - unpriv child main loop: server_loop2(), as usual
- *
- * - protocol:
- * - key exchange packets are always forwarded as is to the monitor
- * - newkeys, record_login(), record_logout() are special packets
- * using the packet type range reserved for local extensions
- *
- * - the child drops privs and runs like a normal sshd, except that it
- * sets dispatch handlers for key exchange packets that forward the
- * packets to the monitor
- *
- * Event loops:
- *
- * - all monitor protocols are synchronous: because the SSHv2 rekey
- * protocols are synchronous and because the other monitor operations
- * are synchronous (or have no replies),
- *
- * - server_loop2() is modified to check the monitor pipe for rekey
- * packets to forward to the client
- *
- * - and dispatch handlers are set, upon receipt of KEXINIT (and reset
- * when NEWKEYS is sent out) to forward incoming rekey packets to the
- * monitor.
- *
- * - the monitor runs an event loop not unlike server_loop2() and runs
- * key exchanges almost exactly as a pre-altprivsep sshd would
- *
- * - unpriv sshd exit -> monitor cleanup (including audit logout) and exit
- *
- * - fatal() in monitor -> forcibly shutdown() socket and kill/wait for
- * child (so that the audit event for the logout better reflects
- * reality -- i.e., logged out means logged out, but for bg jobs)
- *
- * Message formats:
- *
- * - key exchange packets/replies forwarded "as is"
- *
- * - all other monitor requests are sent as SSH2_PRIV_MSG_ALTPRIVSEP and have a
- * sub-type identifier (one octet)
- * - private request sub-types include:
- * - get new shared secret from last re-key
- * - record login (utmpx/wtmpx), request data contains three arguments:
- * pid, ttyname, program name
- * - record logout (utmpx/wtmpx), request data contains one argument: pid
- *
- * Reply sub-types include:
- *
- * - NOP (for record_login/logout)
- * - new shared secret from last re-key
- */
-
-static int aps_started = 0;
-static int is_monitor = 0;
-
-static pid_t monitor_pid, child_pid;
-static int pipe_fds[2];
-static int pipe_fd = -1;
-static Buffer input_pipe, output_pipe; /* for pipe I/O */
-
-static Authctxt *xxx_authctxt;
-
-/* Monitor functions */
-extern void aps_monitor_loop(Authctxt *authctxt, pid_t child_pid);
-static void aps_record_login(void);
-static void aps_record_logout(void);
-static void aps_start_rekex(void);
-Authctxt *aps_read_auth_context(void);
-
-/* main functions for handling the monitor */
-static pid_t altprivsep_start_monitor(Authctxt **authctxt);
-static void altprivsep_do_monitor(Authctxt *authctxt, pid_t child_pid);
-static int altprivsep_started(void);
-static int altprivsep_is_monitor(void);
-
-/* calls _to_ monitor from unprivileged process */
-static void altprivsep_get_newkeys(enum kex_modes mode);
-
-/* monitor-side fatal_cleanup callbacks */
-static void altprivsep_shutdown_sock(void *arg);
-
-/* Altprivsep packet utilities for communication with the monitor */
-static void altprivsep_packet_start(u_char);
-static int altprivsep_packet_send(void);
-static int altprivsep_fwd_packet(u_char type);
-
-static int altprivsep_packet_read(void);
-static void altprivsep_packet_read_expect(int type);
-
-static void altprivsep_packet_put_char(int ch);
-static void altprivsep_packet_put_int(u_int value);
-static void altprivsep_packet_put_cstring(const char *str);
-static void altprivsep_packet_put_raw(const void *buf, u_int len);
-
-static u_int altprivsep_packet_get_char(void);
-static void *altprivsep_packet_get_raw(u_int *length_ptr);
-static void *altprivsep_packet_get_string(u_int *length_ptr);
-
-Kex *prepare_for_ssh2_kex(void);
-
-/*
- * Start monitor from privileged sshd process.
- *
- * Return values are like fork(2); the parent is the monitor. The caller should
- * fatal() on error.
- *
- * Note that the monitor waits until the still privileged child finishes the
- * authentication. The child drops its privileges after the authentication.
- */
-static pid_t
-altprivsep_start_monitor(Authctxt **authctxt)
-{
- pid_t pid;
- int junk;
-
- if (aps_started)
- fatal("Monitor startup failed: missing state");
-
- buffer_init(&output_pipe);
- buffer_init(&input_pipe);
-
- if (pipe(pipe_fds) != 0) {
- error("Monitor startup failure: could not create pipes: %s",
- strerror(errno));
- return (-1);
- }
-
- (void) fcntl(pipe_fds[0], F_SETFD, FD_CLOEXEC);
- (void) fcntl(pipe_fds[1], F_SETFD, FD_CLOEXEC);
-
- monitor_pid = getpid();
-
- if ((pid = fork()) > 0) {
- /*
- * From now on, all debug messages from monitor will have prefix
- * "monitor "
- */
- set_log_txt_prefix("monitor ");
- (void) prepare_for_ssh2_kex();
- packet_set_server();
- /* parent */
- child_pid = pid;
-
- debug2("Monitor pid %ld, unprivileged child pid %ld",
- monitor_pid, child_pid);
-
- (void) close(pipe_fds[1]);
- pipe_fd = pipe_fds[0];
-
- /*
- * Signal readiness of the monitor and then read the
- * authentication context from the child.
- */
- (void) write(pipe_fd, &pid, sizeof (pid));
- packet_set_monitor(pipe_fd);
- xxx_authctxt = *authctxt = aps_read_auth_context();
-
- if (fcntl(pipe_fd, F_SETFL, O_NONBLOCK) < 0)
- error("fcntl O_NONBLOCK: %.100s", strerror(errno));
-
- aps_started = 1;
- is_monitor = 1;
-
- debug2("Monitor started");
-
- return (pid);
- }
-
- if (pid < 0) {
- debug2("Monitor startup failure: could not fork unprivileged"
- " process: %s", strerror(errno));
- return (pid);
- }
-
- /* this is the child that will later drop privileges */
-
- /* note that Solaris has bi-directional pipes so one pipe is enough */
- (void) close(pipe_fds[0]);
- pipe_fd = pipe_fds[1];
-
- /* wait for monitor to be ready */
- debug2("Waiting for monitor");
- (void) read(pipe_fd, &junk, sizeof (junk));
- debug2("Monitor signalled readiness");
-
- buffer_init(&to_monitor);
- buffer_init(&from_monitor);
-
- /* AltPrivSep interfaces are set up */
- aps_started = 1;
- return (pid);
-}
-
-int
-altprivsep_get_pipe_fd(void)
-{
- return (pipe_fd);
-}
-
-/*
- * This function is used in the unprivileged child for all packets in the range
- * between SSH2_MSG_KEXINIT and SSH2_MSG_TRANSPORT_MAX.
- */
-void
-altprivsep_rekey(int type, u_int32_t seq, void *ctxt)
-{
- Kex *kex = (Kex *)ctxt;
-
- if (kex == NULL)
- fatal("Missing key exchange context in unprivileged process");
-
- if (type != SSH2_MSG_NEWKEYS) {
- debug2("Forwarding re-key packet (%d) to monitor", type);
- if (!altprivsep_fwd_packet(type))
- fatal("altprivsep_rekey: Monitor not responding");
- }
-
- /* tell server_loop2() that we're re-keying */
- kex->done = 0;
-
- /* NEWKEYS is special: get the new keys for client->server direction */
- if (type == SSH2_MSG_NEWKEYS) {
- debug2("received SSH2_MSG_NEWKEYS packet - "
- "getting new inbound keys from the monitor");
- altprivsep_get_newkeys(MODE_IN);
- kex->done = 1;
- }
-}
-
-void
-altprivsep_process_input(fd_set *rset)
-{
- void *data;
- int type;
- u_int dlen;
-
- if (pipe_fd == -1)
- return;
-
- if (!FD_ISSET(pipe_fd, rset))
- return;
-
- debug2("reading from pipe to monitor (%d)", pipe_fd);
- if ((type = altprivsep_packet_read()) == -1)
- fatal("altprivsep_process_input: Monitor not responding");
-
- if (!compat20)
- return; /* shouldn't happen! but be safe */
-
- if (type == 0)
- return; /* EOF -- nothing to do here */
-
- if (type >= SSH2_MSG_MAX)
- fatal("Received garbage from monitor");
-
- debug2("Read packet type %d from pipe to monitor", (u_int)type);
-
- if (type == SSH2_PRIV_MSG_ALTPRIVSEP)
- return; /* shouldn't happen! */
-
- /* NEWKEYS is special: get the new keys for server->client direction */
- if (type == SSH2_MSG_NEWKEYS) {
- debug2("forwarding SSH2_MSG_NEWKEYS packet we got from monitor to "
- "the client");
- packet_start(SSH2_MSG_NEWKEYS);
- packet_send();
- debug2("getting new outbound keys from the monitor");
- altprivsep_get_newkeys(MODE_OUT);
- return;
- }
-
- data = altprivsep_packet_get_raw(&dlen);
-
- packet_start((u_char)type);
-
- if (data != NULL && dlen > 0)
- packet_put_raw(data, dlen);
-
- packet_send();
-}
-
-static void
-altprivsep_do_monitor(Authctxt *authctxt, pid_t child_pid)
-{
- aps_monitor_loop(authctxt, child_pid);
-}
-
-static int
-altprivsep_started(void)
-{
- return (aps_started);
-}
-
-static int
-altprivsep_is_monitor(void)
-{
- return (is_monitor);
-}
-
-/*
- * A fatal cleanup function to forcibly shutdown the connection socket
- */
-static void
-altprivsep_shutdown_sock(void *arg)
-{
- int sock;
-
- if (arg == NULL)
- return;
-
- sock = *(int *)arg;
-
- (void) shutdown(sock, SHUT_RDWR);
-}
-
-/* Calls _to_ monitor from unprivileged process */
-static int
-altprivsep_fwd_packet(u_char type)
-{
- u_int len;
- void *data;
-
- altprivsep_packet_start(type);
- data = packet_get_raw(&len);
- altprivsep_packet_put_raw(data, len);
-
- /* packet_send()s any replies from the monitor to the client */
- return (altprivsep_packet_send());
-}
-
-extern Newkeys *current_keys[MODE_MAX];
-
-/* To be called from packet.c:set_newkeys() before referencing current_keys */
-static void
-altprivsep_get_newkeys(enum kex_modes mode)
-{
- Newkeys *newkeys;
- Comp *comp;
- Enc *enc;
- Mac *mac;
- u_int len;
-
- if (!altprivsep_started())
- return;
-
- if (altprivsep_is_monitor())
- return; /* shouldn't happen */
-
- /* request new keys */
- altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- altprivsep_packet_put_char(APS_MSG_NEWKEYS_REQ);
- altprivsep_packet_put_int((u_int)mode);
- altprivsep_packet_send();
- altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
- if (altprivsep_packet_get_char() != APS_MSG_NEWKEYS_REP)
- fatal("Received garbage from monitor during re-keying");
-
- newkeys = xmalloc(sizeof (*newkeys));
- memset(newkeys, 0, sizeof (*newkeys));
-
- enc = &newkeys->enc;
- mac = &newkeys->mac;
- comp = &newkeys->comp;
-
- /* Cipher name, key, IV */
- enc->name = altprivsep_packet_get_string(NULL);
- if ((enc->cipher = cipher_by_name(enc->name)) == NULL)
- fatal("Monitor negotiated an unknown cipher during re-key");
-
- enc->key = altprivsep_packet_get_string(&enc->key_len);
- enc->iv = altprivsep_packet_get_string(&enc->block_size);
-
- /* MAC name */
- mac->name = altprivsep_packet_get_string(NULL);
- if (mac_setup(mac, mac->name) < 0)
- fatal("Monitor negotiated an unknown MAC algorithm "
- "during re-key");
-
- mac->key = altprivsep_packet_get_string(&len);
- if (len > mac->key_len)
- fatal("%s: bad mac key length: %d > %d", __func__, len,
- mac->key_len);
-
- /* Compression algorithm name */
- comp->name = altprivsep_packet_get_string(NULL);
- if (strcmp(comp->name, "zlib") != 0 && strcmp(comp->name, "none") != 0)
- fatal("Monitor negotiated an unknown compression "
- "algorithm during re-key");
-
- comp->type = 0;
- comp->enabled = 0; /* forces compression re-init, as per-spec */
- if (strcmp(comp->name, "zlib") == 0)
- comp->type = 1;
-
- /*
- * Now install new keys
- *
- * For now abuse kex.c/packet.c non-interfaces. Someday, when
- * the many internal interfaces are parametrized, made reentrant
- * and thread-safe, made more consistent, and when necessary-but-
- * currently-missing interfaces are added then this bit of
- * ugliness can be revisited.
- *
- * The ugliness is in the set_newkeys(), its name and the lack
- * of a (Newkeys *) parameter, which forces us to pass the
- * newkeys through current_keys[mode]. But this saves us some
- * lines of code for now, though not comments.
- *
- * Also, we've abused, in the code above, knowledge of what
- * set_newkeys() expects the current_keys[mode] to contain.
- */
- current_keys[mode] = newkeys;
- set_newkeys(mode);
-
-}
-
-void
-altprivsep_record_login(pid_t pid, const char *ttyname)
-{
- altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- altprivsep_packet_put_char(APS_MSG_RECORD_LOGIN);
- altprivsep_packet_put_int(pid);
- altprivsep_packet_put_cstring(ttyname);
- altprivsep_packet_send();
- altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
-}
-
-void
-altprivsep_record_logout(pid_t pid)
-{
- altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- altprivsep_packet_put_char(APS_MSG_RECORD_LOGOUT);
- altprivsep_packet_put_int(pid);
- altprivsep_packet_send();
- altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
-}
-
-void
-altprivsep_start_rekex(void)
-{
- altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- altprivsep_packet_put_char(APS_MSG_START_REKEX);
- altprivsep_packet_send();
- altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
-}
-
-/*
- * The monitor needs some information that its child learns during the
- * authentication process. Since the child was forked before the key exchange
- * and authentication started it must send some context to the monitor after the
- * authentication is finished. Less obvious part - monitor needs the session ID
- * since it is used in the key generation process after the key (re-)exchange is
- * finished.
- */
-void
-altprivsep_send_auth_context(Authctxt *authctxt)
-{
- debug("sending auth context to the monitor");
- altprivsep_packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- altprivsep_packet_put_char(APS_MSG_AUTH_CONTEXT);
- altprivsep_packet_put_int(authctxt->pw->pw_uid);
- altprivsep_packet_put_int(authctxt->pw->pw_gid);
- altprivsep_packet_put_cstring(authctxt->pw->pw_name);
- altprivsep_packet_put_raw(session_id2, session_id2_len);
- debug("will send %d bytes of auth context to the monitor",
- buffer_len(&to_monitor));
- altprivsep_packet_send();
- altprivsep_packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
-}
-
-static void aps_send_newkeys(void);
-
-/* Monitor side dispatch handler for SSH2_PRIV_MSG_ALTPRIVSEP */
-/* ARGSUSED */
-void
-aps_input_altpriv_msg(int type, u_int32_t seq, void *ctxt)
-{
- u_char req_type;
-
- req_type = packet_get_char();
-
- switch (req_type) {
- case APS_MSG_NEWKEYS_REQ:
- aps_send_newkeys();
- break;
- case APS_MSG_RECORD_LOGIN:
- aps_record_login();
- break;
- case APS_MSG_RECORD_LOGOUT:
- aps_record_logout();
- break;
- case APS_MSG_START_REKEX:
- aps_start_rekex();
- break;
- default:
- break;
- }
-}
-
-/* Monitor-side handlers for APS_MSG_* */
-static
-void
-aps_send_newkeys(void)
-{
- Newkeys *newkeys;
- Enc *enc;
- Mac *mac;
- Comp *comp;
- enum kex_modes mode;
-
- /* get direction for which newkeys are wanted */
- mode = (enum kex_modes) packet_get_int();
- packet_check_eom();
-
- /* get those newkeys */
- newkeys = kex_get_newkeys(mode);
- enc = &newkeys->enc;
- mac = &newkeys->mac;
- comp = &newkeys->comp;
-
- /*
- * Negotiated algorithms, client->server and server->client, for
- * cipher, mac and compression.
- */
- packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- packet_put_char(APS_MSG_NEWKEYS_REP);
- packet_put_cstring(enc->name);
- packet_put_string(enc->key, enc->key_len);
- packet_put_string(enc->iv, enc->block_size);
- packet_put_cstring(mac->name);
- packet_put_string(mac->key, mac->key_len);
- packet_put_cstring(comp->name);
-
- packet_send();
- free_keys(newkeys);
-}
-
-struct _aps_login_rec {
- pid_t lr_pid;
- char *lr_tty;
- struct _aps_login_rec *next;
-};
-
-typedef struct _aps_login_rec aps_login_rec;
-
-static aps_login_rec *aps_login_list = NULL;
-
-static
-void
-aps_record_login(void)
-{
- aps_login_rec *new_rec;
- struct stat sbuf;
- size_t proc_path_len;
- char *proc_path;
-
- new_rec = xmalloc(sizeof (aps_login_rec));
- memset(new_rec, 0, sizeof (aps_login_rec));
-
- new_rec->lr_pid = packet_get_int();
- new_rec->lr_tty = packet_get_string(NULL);
-
- proc_path_len = snprintf(NULL, 0, "/proc/%d", new_rec->lr_pid);
- proc_path = xmalloc(proc_path_len + 1);
- (void) snprintf(proc_path, proc_path_len + 1, "/proc/%d",
- new_rec->lr_pid);
-
- if (stat(proc_path, &sbuf) ||
- sbuf.st_uid != xxx_authctxt->pw->pw_uid ||
- stat(new_rec->lr_tty, &sbuf) < 0 ||
- sbuf.st_uid != xxx_authctxt->pw->pw_uid) {
- debug2("Spurious record_login request from unprivileged sshd");
- xfree(proc_path);
- xfree(new_rec->lr_tty);
- xfree(new_rec);
- return;
- }
-
- /* Insert new record on list */
- new_rec->next = aps_login_list;
- aps_login_list = new_rec;
-
- record_login(new_rec->lr_pid, new_rec->lr_tty, NULL,
- xxx_authctxt->user);
-
- packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- packet_send();
-
- xfree(proc_path);
-}
-
-static
-void
-aps_record_logout(void)
-{
- aps_login_rec **p, *q;
- pid_t pid;
-
- pid = packet_get_int();
- packet_check_eom();
-
- for (p = &aps_login_list; *p != NULL; p = &q->next) {
- q = *p;
- if (q->lr_pid == pid) {
- record_logout(q->lr_pid, q->lr_tty, NULL,
- xxx_authctxt->user);
-
- /* dequeue */
- *p = q->next;
- xfree(q->lr_tty);
- xfree(q);
- break;
- }
- }
-
- packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- packet_send();
-}
-
-static
-void
-aps_start_rekex(void)
-{
- /*
- * Send confirmation. We could implement it without that but it doesn't
- * bring any harm to do that and we are consistent with other subtypes
- * of our private SSH2_PRIV_MSG_ALTPRIVSEP message type.
- */
- packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- packet_send();
-
- /*
- * KEX_INIT message could be the one that reached the limit. In that
- * case, it was already forwarded to us from the unnprivileged child,
- * and maybe even acted upon. Obviously we must not send another
- * KEX_INIT message.
- */
- if (!(xxx_kex->flags & KEX_INIT_SENT))
- kex_send_kexinit(xxx_kex);
- else
- debug2("rekeying already in progress");
-}
-
-/*
- * This is the monitor side of altprivsep_send_auth_context().
- */
-Authctxt *
-aps_read_auth_context(void)
-{
- unsigned char *tmp;
- Authctxt *authctxt;
-
- /*
- * After the successful authentication we get the context. Getting
- * end-of-file means that authentication failed and we can exit as well.
- */
- debug("reading the context from the child");
- packet_read_expect(SSH2_PRIV_MSG_ALTPRIVSEP);
- debug3("got SSH2_PRIV_MSG_ALTPRIVSEP");
- if (packet_get_char() != APS_MSG_AUTH_CONTEXT) {
- fatal("APS_MSG_AUTH_CONTEXT message subtype expected.");
- }
-
- authctxt = xcalloc(1, sizeof(Authctxt));
- authctxt->pw = xcalloc(1, sizeof(struct passwd));
-
- /* uid_t and gid_t are integers (UNIX spec) */
- authctxt->pw->pw_uid = packet_get_int();
- authctxt->pw->pw_gid = packet_get_int();
- authctxt->pw->pw_name = packet_get_string(NULL);
- authctxt->user = xstrdup(authctxt->pw->pw_name);
- debug3("uid/gid/username %d/%d/%s", authctxt->pw->pw_uid,
- authctxt->pw->pw_gid, authctxt->user);
- session_id2 = (unsigned char *)packet_get_raw((unsigned int*)&session_id2_len);
-
- /* we don't have this for SSH1. In that case, session_id2_len is 0. */
- if (session_id2_len > 0) {
- tmp = (unsigned char *)xmalloc(session_id2_len);
- memcpy(tmp, session_id2, session_id2_len);
- session_id2 = tmp;
- debug3("read session ID (%d B)", session_id2_len);
- xxx_kex->session_id = tmp;
- xxx_kex->session_id_len = session_id2_len;
- }
- debug("finished reading the context");
-
- /* send confirmation */
- packet_start(SSH2_PRIV_MSG_ALTPRIVSEP);
- packet_send();
-
- return (authctxt);
-}
-
-
-/* Utilities for communication with the monitor */
-static void
-altprivsep_packet_start(u_char type)
-{
- buffer_clear(&to_monitor);
- buffer_put_char(&to_monitor, type);
-}
-
-static void
-altprivsep_packet_put_char(int ch)
-{
- buffer_put_char(&to_monitor, ch);
-}
-
-static void
-altprivsep_packet_put_int(u_int value)
-{
- buffer_put_int(&to_monitor, value);
-}
-
-static void
-altprivsep_packet_put_cstring(const char *str)
-{
- buffer_put_cstring(&to_monitor, str);
-}
-
-static void
-altprivsep_packet_put_raw(const void *buf, u_int len)
-{
- buffer_append(&to_monitor, buf, len);
-}
-
-/*
- * Send a monitor packet to the monitor. This function is blocking.
- *
- * Returns -1 if the monitor pipe has been closed earlier, fatal()s if
- * there's any other problems.
- */
-static int
-altprivsep_packet_send(void)
-{
- ssize_t len;
- u_int32_t plen; /* packet length */
- u_char plen_buf[sizeof (plen)];
- u_char padlen; /* padding length */
- fd_set *setp;
- int err;
-
- if (pipe_fd == -1)
- return (-1);
-
- if ((plen = buffer_len(&to_monitor)) == 0)
- return (0);
-
- /*
- * We talk the SSHv2 binary packet protocol to the monitor,
- * using the none cipher, mac and compression algorithms.
- *
- * But, interestingly, the none cipher has a block size of 8
- * bytes, thus we must pad the packet.
- *
- * Also, encryption includes the packet length, so the padding
- * must account for that field. I.e., (sizeof (packet length) +
- * sizeof (padding length) + packet length + padding length) %
- * block_size must == 0.
- *
- * Also, there must be at least four (4) bytes of padding.
- */
- padlen = (8 - ((plen + sizeof (plen) + sizeof (padlen)) % 8)) % 8;
- if (padlen < 4)
- padlen += 8;
-
- /* packet length counts padding and padding length field */
- plen += padlen + sizeof (padlen);
-
- PUT_32BIT(plen_buf, plen);
-
- setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
- memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
- FD_SET(pipe_fd, setp);
-
- while (select(pipe_fd + 1, NULL, setp, NULL, NULL) == -1) {
- if (errno == EAGAIN || errno == EINTR)
- continue;
- else
- goto pipe_gone;
- }
-
- xfree(setp);
-
- /* packet length field */
- len = atomicio(write, pipe_fd, plen_buf, sizeof (plen));
-
- if (len != sizeof (plen))
- goto pipe_gone;
-
- /* padding length field */
- len = atomicio(write, pipe_fd, &padlen, sizeof (padlen));
-
- if (len != sizeof (padlen))
- goto pipe_gone;
-
- len = atomicio(write, pipe_fd, buffer_ptr(&to_monitor), plen - 1);
-
- if (len != (plen - 1))
- goto pipe_gone;
-
- buffer_clear(&to_monitor);
-
- return (1);
-
-pipe_gone:
-
- err = errno;
-
- (void) close(pipe_fd);
-
- pipe_fd = -1;
-
- fatal("altprvsep_packet_send: Monitor not responding: %.100s",
- strerror(err));
-
- /* NOTREACHED */
- return (0);
-}
-
-/*
- * Read a monitor packet from the monitor. This function is blocking.
- */
-static int
-altprivsep_packet_read(void)
-{
- ssize_t len = -1;
- u_int32_t plen;
- u_char plen_buf[sizeof (plen)];
- u_char padlen;
- fd_set *setp;
- int err;
-
- if (pipe_fd == -1)
- return (-1);
-
- setp = xmalloc(howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
- memset(setp, 0, howmany(pipe_fd + 1, NFDBITS) * sizeof (fd_mask));
- FD_SET(pipe_fd, setp);
-
- while (select(pipe_fd + 1, setp, NULL, NULL, NULL) == -1) {
- if (errno == EAGAIN || errno == EINTR)
- continue;
- else
- goto pipe_gone;
- }
-
- xfree(setp);
-
- /* packet length field */
- len = atomicio(read, pipe_fd, plen_buf, sizeof (plen));
-
- plen = GET_32BIT(plen_buf);
-
- if (len != sizeof (plen))
- goto pipe_gone;
-
- /* padding length field */
- len = atomicio(read, pipe_fd, &padlen, sizeof (padlen));
-
- if (len != sizeof (padlen))
- goto pipe_gone;
-
- plen -= sizeof (padlen);
-
- buffer_clear(&from_monitor);
- buffer_append_space(&from_monitor, plen);
-
- /* packet data + padding */
- len = atomicio(read, pipe_fd, buffer_ptr(&from_monitor), plen);
-
- if (len != plen)
- goto pipe_gone;
-
- /* remove padding */
- if (padlen > 0)
- buffer_consume_end(&from_monitor, padlen);
-
- /* packet type */
- return (buffer_get_char(&from_monitor));
-
-pipe_gone:
-
- err = errno;
-
- (void) close(pipe_fd);
-
- pipe_fd = -1;
-
- if (len < 0)
- fatal("altpriv_packet_read: Monitor not responding %.100s",
- strerror(err));
-
- debug2("Monitor pipe closed by monitor");
- return (0);
-}
-
-static void
-altprivsep_packet_read_expect(int expected)
-{
- int type;
-
- type = altprivsep_packet_read();
-
- if (type <= 0)
- fatal("altprivsep_packet_read_expect: Monitor not responding");
-
- if (type != expected)
- fatal("Protocol error in privilege separation; expected "
- "packet type %d, got %d", expected, type);
-}
-
-static u_int
-altprivsep_packet_get_char(void)
-{
- return (buffer_get_char(&from_monitor));
-}
-void
-*altprivsep_packet_get_raw(u_int *length_ptr)
-{
- if (length_ptr != NULL)
- *length_ptr = buffer_len(&from_monitor);
-
- return (buffer_ptr(&from_monitor));
-}
-void
-*altprivsep_packet_get_string(u_int *length_ptr)
-{
- return (buffer_get_string(&from_monitor, length_ptr));
-}
-
-/*
- * Start and execute the code for the monitor which never returns from this
- * function. The child will return and continue in the caller.
- */
-void
-altprivsep_start_and_do_monitor(int use_engine, int inetd, int newsock,
- int statup_pipe)
-{
- pid_t aps_child;
- Authctxt *authctxt;
-
- /*
- * The monitor will packet_close() in packet_set_monitor() called from
- * altprivsep_start_monitor() below to clean up the socket stuff before
- * it switches to pipes for communication to the child. The socket fd is
- * closed there so we must dup it here - monitor needs that socket to
- * shutdown the connection in case of any problem; see comments below.
- * Note that current newsock was assigned to connection_(in|out) which
- * are the variables used in packet_close() to close the communication
- * socket.
- */
- newsock = dup(newsock);
-
- if ((aps_child = altprivsep_start_monitor(&authctxt)) == -1)
- fatal("Monitor could not be started.");
-
- if (aps_child > 0) {
- /* ALTPRIVSEP Monitor */
-
- /*
- * The ALTPRIVSEP monitor here does:
- *
- * - record keeping and auditing
- * - PAM cleanup
- */
-
- /* this is for MaxStartups and the child takes care of that */
- (void) close(statup_pipe);
- (void) pkcs11_engine_load(use_engine);
-
- /*
- * If the monitor fatal()s it will audit/record a logout, so
- * we'd better do something to really mean it: shutdown the
- * socket but leave the child alone -- it's been disconnected
- * and we hope it exits, but killing any pid from a privileged
- * monitor could be dangerous.
- *
- * NOTE: Order matters -- these fatal cleanups must come before
- * the audit logout fatal cleanup as these functions are called
- * in LIFO.
- */
- fatal_add_cleanup((void (*)(void *))altprivsep_shutdown_sock,
- (void *)&newsock);
-
- if (compat20) {
- debug3("Recording SSHv2 session login in wtmpx");
- /*
- * record_login() relies on connection_in to be the
- * socket to get the peer address. The problem is that
- * connection_in had to be set to the pipe descriptor in
- * altprivsep_start_monitor(). It's not nice but the
- * easiest way to get the peer's address is to
- * temporarily set connection_in to the socket's file
- * descriptor.
- */
- packet_set_fds(inetd == 1 ? -1 : newsock, 0);
- record_login(getpid(), NULL, "sshd", authctxt->user);
- packet_set_fds(0, 1);
- }
-
-#ifdef HAVE_BSM
- /* Initialize the group list, audit sometimes needs it. */
- if (initgroups(authctxt->pw->pw_name,
- authctxt->pw->pw_gid) < 0) {
- perror("initgroups");
- exit (1);
- }
-
- /*
- * The monitor process fork()ed before the authentication
- * process started so at this point we have an unaudited
- * context. Thus we need to obtain the audit session data
- * from the authentication process (aps_child) which will
- * have the correct audit context for the user logging in.
- * To do so we pass along the process-ID of the aps_child
- * process so that it is referenced for this audit session
- * rather than referencing the monitor's unaudited context.
- */
- audit_sshd_login(&ah, aps_child);
-
- fatal_add_cleanup((void (*)(void *))audit_sshd_logout,
- (void *)&ah);
-#endif /* HAVE_BSM */
-
-#ifdef GSSAPI
- fatal_add_cleanup((void (*)(void *))ssh_gssapi_cleanup_creds,
- (void *)&xxx_gssctxt);
-#endif /* GSSAPI */
-
- altprivsep_do_monitor(authctxt, aps_child);
-
- /* If we got here the connection is dead. */
- fatal_remove_cleanup((void (*)(void *))altprivsep_shutdown_sock,
- (void *)&newsock);
-
- if (compat20) {
- debug3("Recording SSHv2 session logout in wtmpx");
- record_logout(getpid(), NULL, "sshd", authctxt->user);
- }
-
- /*
- * Make sure the socket is closed. The monitor can't call
- * packet_close here as it's done a packet_set_connection()
- * with the pipe to the child instead of the socket.
- */
- (void) shutdown(newsock, SHUT_RDWR);
-
-#ifdef GSSAPI
- fatal_remove_cleanup((void (*)(void *))ssh_gssapi_cleanup_creds,
- &xxx_gssctxt);
- ssh_gssapi_cleanup_creds(xxx_gssctxt);
- ssh_gssapi_server_mechs(NULL); /* release cached mechs list */
-#endif /* GSSAPI */
-
-#ifdef HAVE_BSM
- fatal_remove_cleanup((void (*)(void *))audit_sshd_logout, (void *)&ah);
- audit_sshd_logout(&ah);
-#endif /* HAVE_BSM */
-
- exit(0);
- } else {
- /*
- * This is the child, close the dup()ed file descriptor for a
- * socket. It's not needed in the child.
- */
- close(newsock);
- }
-}