CVE-2016-10012 (C, published 2017-01-05T02:59Z)
Description: value: The shared memory manager (associated with pre-authentication compression) in sshd in OpenSSH before 7.4 does not ensure that a bounds check is enforced by all compilers, which might allows local users to gain privileges by leveraging access to a sandboxed privilege-separation process, related to the m_zback and m_zlib data structures.
Severity: HIGH (CVSS v3 base score: 7.8)
Weakness: CWE-119 \u2014 Improper Restriction of Operations within the Bounds of a Memory Buffer
The product performs operations on a memory buffer, but it reads from or writes to a memory location outside the buffer's intended boundary. This may result in read or write operations on unexpected memory locations that could be linked to other variables, data structures, or internal program data.
Repository: https://github.com/openbsd/src
Files changed:
usr.bin/ssh/monitor.c
usr.bin/ssh/monitor.h
usr.bin/ssh/monitor_wrap.h
usr.bin/ssh/myproposal.h
usr.bin/ssh/opacket.h
usr.bin/ssh/packet.c
usr.bin/ssh/packet.h
usr.bin/ssh/servconf.c
usr.bin/ssh/sshconnect2.c
usr.bin/ssh/sshd.c
usr.bin/ssh/sshd/Makefile
Commit message: Remove support for pre-authentication compression. Doing compression
early in the protocol probably seemed reasonable in the 1990s, but
today it's clearly a bad idea in terms of both cryptography (cf.
multiple compression oracle attacks in TLS) and attack surface.

Moreover, to support it across privilege-separation zlib needed
the assistance of a complex shared-memory manager that made the
required attack surface considerably larger.

Prompted by Guido Vranken pointing out a compiler-elided security
check in the shared memory manager found by Stack
(http://css.csail.mit.edu/stack/); ok deraadt@ markus@

NB. pre-auth authentication has been disabled by default in sshd
for >10 years.


Vulnerable code:
/* $OpenBSD: monitor.c,v 1.165 2016/09/05 13:57:31 djm Exp $ */
#include "monitor_mm.h"
void
monitor_sync(struct monitor *pmonitor)
{
	if (options.compression) {
		/* The member allocation is not visible, so sync it */
		mm_share_sync(&pmonitor->m_zlib, &pmonitor->m_zback);
	}
}

/* Allocation functions for zlib */
static void *
mm_zalloc(struct mm_master *mm, u_int ncount, u_int size)
{
	if (size == 0 || ncount == 0 || ncount > SIZE_MAX / size)
		fatal("%s: mm_zalloc(%u, %u)", __func__, ncount, size);

	return mm_malloc(mm, size * ncount);
}

static void
mm_zfree(struct mm_master *mm, void *address)
{
	mm_free(mm, address);
}


	/* Update with new address */
	if (options.compression) {
		ssh_packet_set_compress_hooks(ssh, pmonitor->m_zlib,
		    (ssh_packet_comp_alloc_func *)mm_zalloc,
		    (ssh_packet_comp_free_func *)mm_zfree);
	}
	struct ssh *ssh = active_state;			/* XXX */

	/* Used to share zlib space across processes */
	if (options.compression) {
		mon->m_zback = mm_create(NULL, MM_MEMSIZE);
		mon->m_zlib = mm_create(mon->m_zback, 20 * MM_MEMSIZE);

		/* Compression needs to share state across borders */
		ssh_packet_set_compress_hooks(ssh, mon->m_zlib,
		    (ssh_packet_comp_alloc_func *)mm_zalloc,
		    (ssh_packet_comp_free_func *)mm_zfree);
	}

/* $OpenBSD: monitor.h,v 1.19 2015/01/19 19:52:16 markus Exp $ */
struct mm_master;
	struct mm_master	*m_zback;
	struct mm_master	*m_zlib;
void monitor_sync(struct monitor *);
/* $OpenBSD: monitor_mm.c,v 1.21 2015/02/06 23:21:59 millert Exp $ */
/*
 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include <sys/types.h>
#include <sys/mman.h>
#include <sys/tree.h>

#include <errno.h>
#include <stdarg.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>
#include <string.h>

#include "xmalloc.h"
#include "ssh.h"
#include "log.h"
#include "monitor_mm.h"

static int
mm_compare(struct mm_share *a, struct mm_share *b)
{
	ptrdiff_t diff = (char *)a->address - (char *)b->address;

	if (diff == 0)
		return (0);
	else if (diff < 0)
		return (-1);
	else
		return (1);
}

RB_GENERATE(mmtree, mm_share, next, mm_compare)

static struct mm_share *
mm_make_entry(struct mm_master *mm, struct mmtree *head,
    void *address, size_t size)
{
	struct mm_share *tmp, *tmp2;

	if (mm->mmalloc == NULL)
		tmp = xcalloc(1, sizeof(struct mm_share));
	else
		tmp = mm_xmalloc(mm->mmalloc, sizeof(struct mm_share));
	tmp->address = address;
	tmp->size = size;

	tmp2 = RB_INSERT(mmtree, head, tmp);
	if (tmp2 != NULL)
		fatal("mm_make_entry(%p): double address %p->%p(%zu)",
		    mm, tmp2, address, size);

	return (tmp);
}

/* Creates a shared memory area of a certain size */

struct mm_master *
mm_create(struct mm_master *mmalloc, size_t size)
{
	void *address;
	struct mm_master *mm;

	if (mmalloc == NULL)
		mm = xcalloc(1, sizeof(struct mm_master));
	else
		mm = mm_xmalloc(mmalloc, sizeof(struct mm_master));

	/*
	 * If the memory map has a mm_master it can be completely
	 * shared including authentication between the child
	 * and the client.
	 */
	mm->mmalloc = mmalloc;

	address = mmap(NULL, size, PROT_WRITE|PROT_READ, MAP_ANON|MAP_SHARED,
	    -1, 0);
	if (address == MAP_FAILED)
		fatal("mmap(%zu): %s", size, strerror(errno));

	mm->address = address;
	mm->size = size;

	RB_INIT(&mm->rb_free);
	RB_INIT(&mm->rb_allocated);

	mm_make_entry(mm, &mm->rb_free, address, size);

	return (mm);
}

/* Frees either the allocated or the free list */

static void
mm_freelist(struct mm_master *mmalloc, struct mmtree *head)
{
	struct mm_share *mms, *next;

	for (mms = RB_ROOT(head); mms; mms = next) {
		next = RB_NEXT(mmtree, head, mms);
		RB_REMOVE(mmtree, head, mms);
		if (mmalloc == NULL)
			free(mms);
		else
			mm_free(mmalloc, mms);
	}
}

/* Destroys a memory mapped area */

void
mm_destroy(struct mm_master *mm)
{
	mm_freelist(mm->mmalloc, &mm->rb_free);
	mm_freelist(mm->mmalloc, &mm->rb_allocated);

	if (munmap(mm->address, mm->size) == -1)
		fatal("munmap(%p, %zu): %s", mm->address, mm->size,
		    strerror(errno));
	if (mm->mmalloc == NULL)
		free(mm);
	else
		mm_free(mm->mmalloc, mm);
}

void *
mm_xmalloc(struct mm_master *mm, size_t size)
{
	void *address;

	address = mm_malloc(mm, size);
	if (address == NULL)
		fatal("%s: mm_malloc(%zu)", __func__, size);
	memset(address, 0, size);
	return (address);
}


/* Allocates data from a memory mapped area */

void *
mm_malloc(struct mm_master *mm, size_t size)
{
	struct mm_share *mms, *tmp;

	if (size == 0)
		fatal("mm_malloc: try to allocate 0 space");
	if (size > SIZE_MAX - MM_MINSIZE + 1)
		fatal("mm_malloc: size too big");

	size = ((size + (MM_MINSIZE - 1)) / MM_MINSIZE) * MM_MINSIZE;

	RB_FOREACH(mms, mmtree, &mm->rb_free) {
		if (mms->size >= size)
			break;
	}

	if (mms == NULL)
		return (NULL);

	/* Debug */
	memset(mms->address, 0xd0, size);

	tmp = mm_make_entry(mm, &mm->rb_allocated, mms->address, size);

	/* Does not change order in RB tree */
	mms->size -= size;
	mms->address = (char *)mms->address + size;

	if (mms->size == 0) {
		RB_REMOVE(mmtree, &mm->rb_free, mms);
		if (mm->mmalloc == NULL)
			free(mms);
		else
			mm_free(mm->mmalloc, mms);
	}

	return (tmp->address);
}

/* Frees memory in a memory mapped area */

void
mm_free(struct mm_master *mm, void *address)
{
	struct mm_share *mms, *prev, tmp;

	tmp.address = address;
	mms = RB_FIND(mmtree, &mm->rb_allocated, &tmp);
	if (mms == NULL)
		fatal("mm_free(%p): can not find %p", mm, address);

	/* Debug */
	memset(mms->address, 0xd0, mms->size);

	/* Remove from allocated list and insert in free list */
	RB_REMOVE(mmtree, &mm->rb_allocated, mms);
	if (RB_INSERT(mmtree, &mm->rb_free, mms) != NULL)
		fatal("mm_free(%p): double address %p", mm, address);

	/* Find previous entry */
	prev = mms;
	if (RB_LEFT(prev, next)) {
		prev = RB_LEFT(prev, next);
		while (RB_RIGHT(prev, next))
			prev = RB_RIGHT(prev, next);
	} else {
		if (RB_PARENT(prev, next) &&
		    (prev == RB_RIGHT(RB_PARENT(prev, next), next)))
			prev = RB_PARENT(prev, next);
		else {
			while (RB_PARENT(prev, next) &&
			    (prev == RB_LEFT(RB_PARENT(prev, next), next)))
				prev = RB_PARENT(prev, next);
			prev = RB_PARENT(prev, next);
		}
	}

	/* Check if range does not overlap */
	if (prev != NULL && MM_ADDRESS_END(prev) > address)
		fatal("mm_free: memory corruption: %p(%zu) > %p",
		    prev->address, prev->size, address);

	/* See if we can merge backwards */
	if (prev != NULL && MM_ADDRESS_END(prev) == address) {
		prev->size += mms->size;
		RB_REMOVE(mmtree, &mm->rb_free, mms);
		if (mm->mmalloc == NULL)
			free(mms);
		else
			mm_free(mm->mmalloc, mms);
	} else
		prev = mms;

	if (prev == NULL)
		return;

	/* Check if we can merge forwards */
	mms = RB_NEXT(mmtree, &mm->rb_free, prev);
	if (mms == NULL)
		return;

	if (MM_ADDRESS_END(prev) > mms->address)
		fatal("mm_free: memory corruption: %p < %p(%zu)",
		    mms->address, prev->address, prev->size);
	if (MM_ADDRESS_END(prev) != mms->address)
		return;

	prev->size += mms->size;
	RB_REMOVE(mmtree, &mm->rb_free, mms);

	if (mm->mmalloc == NULL)
		free(mms);
	else
		mm_free(mm->mmalloc, mms);
}

static void
mm_sync_list(struct mmtree *oldtree, struct mmtree *newtree,
    struct mm_master *mm, struct mm_master *mmold)
{
	struct mm_master *mmalloc = mm->mmalloc;
	struct mm_share *mms, *new;

	/* Sync free list */
	RB_FOREACH(mms, mmtree, oldtree) {
		/* Check the values */
		mm_memvalid(mmold, mms, sizeof(struct mm_share));
		mm_memvalid(mm, mms->address, mms->size);

		new = mm_xmalloc(mmalloc, sizeof(struct mm_share));
		memcpy(new, mms, sizeof(struct mm_share));
		RB_INSERT(mmtree, newtree, new);
	}
}

void
mm_share_sync(struct mm_master **pmm, struct mm_master **pmmalloc)
{
	struct mm_master *mm;
	struct mm_master *mmalloc;
	struct mm_master *mmold;
	struct mmtree rb_free, rb_allocated;

	debug3("%s: Share sync", __func__);

	mm = *pmm;
	mmold = mm->mmalloc;
	mm_memvalid(mmold, mm, sizeof(*mm));

	mmalloc = mm_create(NULL, mm->size);
	mm = mm_xmalloc(mmalloc, sizeof(struct mm_master));
	memcpy(mm, *pmm, sizeof(struct mm_master));
	mm->mmalloc = mmalloc;

	rb_free = mm->rb_free;
	rb_allocated = mm->rb_allocated;

	RB_INIT(&mm->rb_free);
	RB_INIT(&mm->rb_allocated);

	mm_sync_list(&rb_free, &mm->rb_free, mm, mmold);
	mm_sync_list(&rb_allocated, &mm->rb_allocated, mm, mmold);

	mm_destroy(mmold);

	*pmm = mm;
	*pmmalloc = mmalloc;

	debug3("%s: Share sync end", __func__);
}

void
mm_memvalid(struct mm_master *mm, void *address, size_t size)
{
	void *end = (char *)address + size;

	if (address < mm->address)
		fatal("mm_memvalid: address too small: %p", address);
	if (end < address)
		fatal("mm_memvalid: end < address: %p < %p", end, address);
	if (end > MM_ADDRESS_END(mm))
		fatal("mm_memvalid: address too large: %p", address);
}
/* $OpenBSD: monitor_mm.h,v 1.6 2014/01/04 17:50:55 tedu Exp $ */

/*
 * Copyright 2002 Niels Provos <provos@citi.umich.edu>
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _MM_H_
#define _MM_H_

struct mm_share {
	RB_ENTRY(mm_share) next;
	void *address;
	size_t size;
};

struct mm_master {
	RB_HEAD(mmtree, mm_share) rb_free;
	struct mmtree rb_allocated;
	void *address;
	size_t size;

	struct mm_master *mmalloc;	/* Used to completely share */
};

RB_PROTOTYPE(mmtree, mm_share, next, mm_compare)

#define MM_MINSIZE		128

#define MM_ADDRESS_END(x)	(void *)((char *)(x)->address + (x)->size)

struct mm_master *mm_create(struct mm_master *, size_t);
void mm_destroy(struct mm_master *);

void mm_share_sync(struct mm_master **, struct mm_master **);

void *mm_malloc(struct mm_master *, size_t);
void *mm_xmalloc(struct mm_master *, size_t);
void mm_free(struct mm_master *, void *);

void mm_memvalid(struct mm_master *, void *, size_t);
#endif /* _MM_H_ */
/* $OpenBSD: monitor_wrap.h,v 1.31 2016/08/13 17:47:41 markus Exp $ */
/* zlib allocation hooks */
void mm_init_compression(struct mm_master *);

/* $OpenBSD: myproposal.h,v 1.53 2016/09/22 17:52:53 djm Exp $ */
#define	KEX_DEFAULT_COMP	"none,zlib@openssh.com,zlib"
#define packet_set_compress_hooks(ctx, allocfunc, freefunc) \
	ssh_packet_set_compress_hooks(active_state, ctx, \
	    allocfunc, freefunc);
/* $OpenBSD: packet.c,v 1.238 2016/09/19 19:02:19 markus Exp $ */
/* Serialise compression state into a blob for privsep */
static int
ssh_packet_get_compress_state(struct sshbuf *m, struct ssh *ssh)
{
	struct session_state *state = ssh->state;
	struct sshbuf *b;
	int r;

	if ((b = sshbuf_new()) == NULL)
		return SSH_ERR_ALLOC_FAIL;
	if (state->compression_in_started) {
		if ((r = sshbuf_put_string(b, &state->compression_in_stream,
		    sizeof(state->compression_in_stream))) != 0)
			goto out;
	} else if ((r = sshbuf_put_string(b, NULL, 0)) != 0)
		goto out;
	if (state->compression_out_started) {
		if ((r = sshbuf_put_string(b, &state->compression_out_stream,
		    sizeof(state->compression_out_stream))) != 0)
			goto out;
	} else if ((r = sshbuf_put_string(b, NULL, 0)) != 0)
		goto out;
	r = sshbuf_put_stringb(m, b);
 out:
	sshbuf_free(b);
	return r;
}

/* Deserialise compression state from a blob for privsep */
static int
ssh_packet_set_compress_state(struct ssh *ssh, struct sshbuf *m)
{
	struct session_state *state = ssh->state;
	struct sshbuf *b = NULL;
	int r;
	const u_char *inblob, *outblob;
	size_t inl, outl;

	if ((r = sshbuf_froms(m, &b)) != 0)
		goto out;
	if ((r = sshbuf_get_string_direct(b, &inblob, &inl)) != 0 ||
	    (r = sshbuf_get_string_direct(b, &outblob, &outl)) != 0)
		goto out;
	if (inl == 0)
		state->compression_in_started = 0;
	else if (inl != sizeof(state->compression_in_stream)) {
		r = SSH_ERR_INTERNAL_ERROR;
		goto out;
	} else {
		state->compression_in_started = 1;
		memcpy(&state->compression_in_stream, inblob, inl);
	}
	if (outl == 0)
		state->compression_out_started = 0;
	else if (outl != sizeof(state->compression_out_stream)) {
		r = SSH_ERR_INTERNAL_ERROR;
		goto out;
	} else {
		state->compression_out_started = 1;
		memcpy(&state->compression_out_stream, outblob, outl);
	}
	r = 0;
 out:
	sshbuf_free(b);
	return r;
}

void
ssh_packet_set_compress_hooks(struct ssh *ssh, void *ctx,
    void *(*allocfunc)(void *, u_int, u_int),
    void (*freefunc)(void *, void *))
{
	ssh->state->compression_out_stream.zalloc = (alloc_func)allocfunc;
	ssh->state->compression_out_stream.zfree = (free_func)freefunc;
	ssh->state->compression_out_stream.opaque = ctx;
	ssh->state->compression_in_stream.zalloc = (alloc_func)allocfunc;
	ssh->state->compression_in_stream.zfree = (free_func)freefunc;
	ssh->state->compression_in_stream.opaque = ctx;
}

	struct sshcomp *comp;
	int r, mode;
	for (mode = 0; mode < MODE_MAX; mode++) {
		if (ssh->state->newkeys[mode] == NULL)
			continue;
		comp = &ssh->state->newkeys[mode]->comp;
		if (comp && comp->enabled &&
		    (r = ssh_packet_init_compression(ssh)) != 0)
			return r;
	}
	    (r = sshbuf_put_u32(b, comp->enabled)) != 0 ||

	if ((r = ssh_packet_get_compress_state(m, ssh)) != 0 ||
	    (r = sshbuf_put_stringb(m, state->input)) != 0 ||
	    (r = sshbuf_get_u32(b, (u_int *)&comp->enabled)) != 0 ||
	if ((r = ssh_packet_set_compress_state(ssh, m)) != 0 ||
	    (r = ssh_packet_set_postauth(ssh)) != 0)
/* $OpenBSD: packet.h,v 1.71 2016/03/07 19:02:43 djm Exp $ */
typedef void *(ssh_packet_comp_alloc_func)(void *, u_int, u_int);
typedef void (ssh_packet_comp_free_func)(void *, void *);
void	 ssh_packet_set_compress_hooks(struct ssh *, void *,
    ssh_packet_comp_alloc_func *, ssh_packet_comp_free_func *);

/* $OpenBSD: servconf.c,v 1.295 2016/08/25 23:57:54 djm Exp $ */
	{ "yes",			COMP_ZLIB },
/* $OpenBSD: sshconnect2.c,v 1.248 2016/09/22 02:29:57 dtucker Exp $ */
	    "zlib@openssh.com,zlib,none" : "none,zlib@openssh.com,zlib";
/* $OpenBSD: sshd.c,v 1.475 2016/08/28 22:28:12 djm Exp $ */
#include "monitor_mm.h"
		/* Sync memory */
		monitor_sync(pmonitor);

	} else if (options.compression == COMP_DELAYED) {
		myproposal[PROPOSAL_COMP_ALGS_CTOS] =
		    myproposal[PROPOSAL_COMP_ALGS_STOC] =
		    "none,zlib@openssh.com";
#	$OpenBSD: Makefile,v 1.89 2016/08/13 17:47:41 markus Exp $
	monitor_mm.c monitor.c monitor_wrap.c \

Fixed code:
/* $OpenBSD: monitor.c,v 1.166 2016/09/28 16:33:06 djm Exp $ */
/* $OpenBSD: monitor.h,v 1.20 2016/09/28 16:33:07 djm Exp $ */
/* $OpenBSD: monitor_wrap.h,v 1.32 2016/09/28 16:33:07 djm Exp $ */
/* $OpenBSD: myproposal.h,v 1.54 2016/09/28 16:33:07 djm Exp $ */
#define	KEX_DEFAULT_COMP	"none,zlib@openssh.com"
/* $OpenBSD: packet.c,v 1.239 2016/09/28 16:33:07 djm Exp $ */
	int r;
	if ((r = ssh_packet_enable_delayed_compress(ssh)) != 0)
		return r;
	if ((r = sshbuf_put_stringb(m, state->input)) != 0 ||
	if ((r = ssh_packet_set_postauth(ssh)) != 0)
/* $OpenBSD: packet.h,v 1.72 2016/09/28 16:33:07 djm Exp $ */
/* $OpenBSD: servconf.c,v 1.296 2016/09/28 16:33:07 djm Exp $ */
	{ "yes",			COMP_DELAYED },
/* $OpenBSD: sshconnect2.c,v 1.249 2016/09/28 16:33:07 djm Exp $ */
	    "zlib@openssh.com,none" : "none,zlib@openssh.com";
/* $OpenBSD: sshd.c,v 1.476 2016/09/28 16:33:07 djm Exp $ */
#	$OpenBSD: Makefile,v 1.90 2016/09/28 16:33:07 djm Exp $
	monitor.c monitor_wrap.c \