#!/usr/local/bin/php
<?php

/*
 * Copyright (c) 2015-2018 Franco Fichtner <franco@opnsense.org>
 *
 * 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 AND CONTRIBUTORS ``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 OR CONTRIBUTORS 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.
 */

$etc_group = <<<EOF
# \$FreeBSD$
#
wheel:*:0:root
daemon:*:1:
kmem:*:2:
sys:*:3:
tty:*:4:
operator:*:5:root
mail:*:6:
bin:*:7:
news:*:8:
man:*:9:
games:*:13:
ftp:*:14:
staff:*:20:
sshd:*:22:
smmsp:*:25:
mailnull:*:26:
guest:*:31:
video:*:44:
bind:*:53:
unbound:*:59:
proxy:*:62:
authpf:*:63:
_pflogd:*:64:
_dhcp:*:65:
uucp:*:66:
dialer:*:68:
network:*:69:
audit:*:77:
www:*:80:
_ypldap:*:160:
hast:*:845:
nogroup:*:65533:
nobody:*:65534:

EOF;

$etc_master_passwd = <<<EOF
# \$FreeBSD$
#
root::0:0::0:0:Charlie &:/root:/bin/csh
toor:*:0:0::0:0:Bourne-again Superuser:/root:
installer:*:0:0::0:0:Installer Superuser:/root:/usr/sbin/nologin
daemon:*:1:1::0:0:Owner of many system processes:/root:/usr/sbin/nologin
operator:*:2:5::0:0:System &:/:/usr/sbin/nologin
bin:*:3:7::0:0:Binaries Commands and Source:/:/usr/sbin/nologin
tty:*:4:65533::0:0:Tty Sandbox:/:/usr/sbin/nologin
kmem:*:5:65533::0:0:KMem Sandbox:/:/usr/sbin/nologin
games:*:7:13::0:0:Games pseudo-user:/:/usr/sbin/nologin
news:*:8:8::0:0:News Subsystem:/:/usr/sbin/nologin
man:*:9:9::0:0:Mister Man Pages:/usr/share/man:/usr/sbin/nologin
sshd:*:22:22::0:0:Secure Shell Daemon:/var/empty:/usr/sbin/nologin
smmsp:*:25:25::0:0:Sendmail Submission User:/var/spool/clientmqueue:/usr/sbin/nologin
mailnull:*:26:26::0:0:Sendmail Default User:/var/spool/mqueue:/usr/sbin/nologin
bind:*:53:53::0:0:Bind Sandbox:/:/usr/sbin/nologin
unbound:*:59:59::0:0:Unbound DNS Resolver:/var/unbound:/usr/sbin/nologin
proxy:*:62:62::0:0:Packet Filter pseudo-user:/nonexistent:/usr/sbin/nologin
_pflogd:*:64:64::0:0:pflogd privsep user:/var/empty:/usr/sbin/nologin
_dhcp:*:65:65::0:0:dhcp programs:/var/empty:/usr/sbin/nologin
uucp:*:66:66::0:0:UUCP pseudo-user:/var/spool/uucppublic:/usr/local/libexec/uucp/uucico
pop:*:68:6::0:0:Post Office Owner:/nonexistent:/usr/sbin/nologin
auditdistd:*:78:77::0:0:Auditdistd unprivileged user:/var/empty:/usr/sbin/nologin
www:*:80:80::0:0:World Wide Web Owner:/nonexistent:/usr/sbin/nologin
_ypldap:*:160:160::0:0:YP LDAP unprivileged user:/var/empty:/usr/sbin/nologin
hast:*:845:845::0:0:HAST unprivileged user:/var/empty:/usr/sbin/nologin
nobody:*:65534:65534::0:0:Unprivileged user:/nonexistent:/usr/sbin/nologin

EOF;

$etc_shells = <<<EOF
# \$FreeBSD$
#
# List of acceptable shells for chpass(1).
# Ftpd will not allow users to connect who are not using
# one of these shells.


EOF;

$known_shells = array(
    '/bin/csh',
    '/bin/sh',
    '/bin/tcsh',
    '/usr/local/bin/bash',
    '/usr/local/bin/scponly',
    '/usr/local/bin/zsh',
    '/usr/local/etc/rc.initial',
    '/usr/local/etc/rc.installer',
);

foreach ($known_shells as $shell) {
    if (file_exists($shell)) {
        $etc_shells .= "{$shell}\n";
    }
}

$etc_ttys = <<<EOF
#
# \$FreeBSD$
#	@(#)ttys	5.1 (Berkeley) 4/17/89
#
# This file specifies various information about terminals on the system.
# It is used by several different programs.  Common entries for the
# various columns include:
#
# name  The name of the terminal device.
#
# getty The program to start running on the terminal.  Typically a
#       getty program, as the name implies.  Other common entries
#       include none, when no getty is needed, and xdm, to start the
#       X Window System.
#
# type The initial terminal type for this port.  For hardwired
#      terminal lines, this will contain the type of terminal used.
#      For virtual consoles, the correct type is typically xterm.
#      Other common values include dialup for incoming modem ports, and
#      unknown when the terminal type cannot be predetermined.
#
# status Must be on or off.  If on, init will run the getty program on
#        the specified port.  If the word "secure" appears, this tty
#        allows root login.
#
# name	getty				type	status		comments
#
# If console is marked "insecure", then init will ask for the root password
# when going to single-user mode.
console	none				unknown	off secure
#
ttyv0	"/usr/libexec/getty Pc"		xterm	on  secure
# Virtual terminals
ttyv1	"/usr/libexec/getty Pc"		xterm	on  secure
ttyv2	"/usr/libexec/getty Pc"		xterm	on  secure
ttyv3	"/usr/libexec/getty Pc"		xterm	on  secure
ttyv4	"/usr/libexec/getty Pc"		xterm	on  secure
ttyv5	"/usr/libexec/getty Pc"		xterm	on  secure
ttyv6	"/usr/libexec/getty Pc"		xterm	on  secure
ttyv7	"/usr/libexec/getty Pc"		xterm	on  secure
ttyv8	"/usr/local/bin/xdm -nodaemon"	xterm	off secure
# Serial terminals
# The 'dialup' keyword identifies dialin lines to login, fingerd etc.
ttyu0	"/usr/libexec/getty 3wire"	vt100	onifconsole secure
ttyu1	"/usr/libexec/getty 3wire"	vt100	onifconsole secure
ttyu2	"/usr/libexec/getty 3wire"	vt100	onifconsole secure
ttyu3	"/usr/libexec/getty 3wire"	vt100	onifconsole secure
# Dumb console
dcons	"/usr/libexec/getty std.9600"	vt100	off secure

EOF;

$php_ini = <<<EOF
; File generated via recovery
output_buffering = "0"
expose_php = Off
implicit_flush = true
magic_quotes_gpc = Off
max_execution_time = 900
max_input_time = 1800
max_input_vars = 5000
memory_limit = 384M
register_argc_argv = On
register_long_arrays = Off
variables_order = "GPCS"
file_uploads = On
upload_tmp_dir = /tmp
upload_max_filesize = 200M
post_max_size = 200M
html_errors = Off
zlib.output_compression = Off
zlib.output_compression_level = 1
include_path = ".:/usr/local/etc/inc:/usr/local/www:/usr/local/opnsense/mvc:/usr/local/share/pear:/usr/local/opnsense/contrib"
ignore_repeated_errors = on
error_reporting = E_ALL ^ (E_NOTICE | E_DEPRECATED | E_STRICT)
display_errors=on
log_errors=on
error_log=/tmp/PHP_errors.log
date.timezone="Etc/UTC"

EOF;

function recover_ports()
{
	$actions = array(
		'pre-install',
		'post-install'
	);

	exec('/usr/local/sbin/pkg-static query -e \'%#U > 0 || %#G > 0\' %n', $pkgs);

	if ($pkgs[0] == '') {
		return;
	}

	$tempname = tempnam('/tmp', 'recover.');
	$tempfile = fopen($tempname, 'w');

	foreach ($pkgs as $pkg) {
		$raw = exec('/usr/local/sbin/pkg-static info --raw --raw-format json-compact ' . $pkg);
		$info = json_decode($raw, true);
		foreach($actions as $action) {
			if (!isset($info['scripts'][$action])) {
				continue;
			}

			ftruncate($tempfile, 0);
			rewind($tempfile);
			fwrite($tempfile, $info['scripts'][$action] . PHP_EOL);
			passthru('/bin/sh ' . $tempname);
		}
	}

	fclose($tempfile);
	unlink($tempname);
}

function recover_rebuild()
{
	passthru('/usr/sbin/pwd_mkdb -p /etc/master.passwd');
	passthru('/usr/sbin/pwd_mkdb /etc/master.passwd');
	passthru('/bin/sync');
}

function recover_base($etc_group, $etc_master_passwd, $etc_shells, $etc_ttys, $php_ini)
{
	echo "===> Restoring /etc/group\n";
	file_put_contents('/etc/group', $etc_group);

	echo "===> Restoring /etc/master.passwd\n";
	file_put_contents('/etc/master.passwd', $etc_master_passwd);

	echo "===> Restoring /etc/shells\n";
	file_put_contents('/etc/shells', $etc_shells);

	echo "===> Restoring /etc/ttys\n";
	file_put_contents('/etc/ttys', $etc_ttys);

	echo "===> Restoring php.ini files\n";
	file_put_contents('/usr/local/etc/php.ini', $php_ini);
	file_put_contents('/usr/local/lib/php.ini', $php_ini);

	recover_rebuild();
}

function recover_pkg()
{
	recover_ports();
	recover_rebuild();
}

$stage = isset($argv[1]) ? $argv[1] : 'both';

switch ($stage) {
	case 'base':
		recover_base($etc_group, $etc_master_passwd, $etc_shells, $etc_ttys, $php_ini);
		break;
	case 'pkg':
		recover_pkg();
		break;
	default:
		recover_base($etc_group, $etc_master_passwd, $etc_shells, $etc_ttys, $php_ini);
		recover_pkg();
		break;
}
