#!/bin/bash # cantboot breakboot v0.1.7 last mod 2016/09/05 # Latest version at # Copyright 2016 Ryan Sawhill Aroha & cantboot contributors # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation, either version 3 of the License, or # (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU # General Public License for more details. #------------------------------------------------------------------------------- [[ ${1} == --silentcheck ]] && exit 77 sysargv=( "${@}" ) # Get version from line #2 version=$(sed '2q;d' ${0}) # If "gitRoot" variable is unset, it will default to github/ryran/cantboot/master : ${gitRoot:="https://raw.githubusercontent.com/ryran/cantboot/master"} # For testing, customize the above via environment variables, for example: # ~]$ gitRoot=https://raw.githubusercontent.com/YOURNAME/cantboot/YOURFEAT ./breakboot --ls # Colors c0='\033[0;0m' cB='\033[1;1m' cR='\033[1;31m' cY='\033[1;33m' cG='\033[1;32m' cg='\033[0;32m' cC='\033[1;36m' cc='\033[0;36m' BOLD() { echo -e "${cB}${@}${c0}"; } RED() { echo -e "${cR}${@}${c0}"; } YELLOW() { echo -e "${cY}${@}${c0}"; } GREEN() { echo -e "${cG}${@}${c0}"; } green() { echo -e "${cg}${@}${c0}"; } CYAN() { echo -e "${cC}${@}${c0}"; } cyan() { echo -e "${cc}${@}${c0}"; } UPDATE() { cyan "Downloading ${gitRoot}/breakboot ..." if curl -o ./breakboot.tmp -LsSk ${gitRoot}/breakboot; then chmod +x ./breakboot.tmp ./breakboot.tmp --silentcheck &>/dev/null if [[ ${?} != 77 ]]; then RED "Download failed; unable to update" exit 1 fi if mv -v ./breakboot.tmp "${0}"; then GREEN "Updated "${0}" successfully" else RED "Error moving downloaded file ('${PWD}/breakboot.tmp') to original script location ('${0}')" exit 1 fi if [[ ${sysargv[0]} == --update ]]; then echo "Exiting" exit else echo "Re-executing with same args ..." exec "${0}" "${sysargv[@]}" fi else RED "Download failed; unable to update" exit 1 fi } USAGE() { echo -e "${cB}Usage: ${0##*/} { --ls | --ll | [--lesson] | --update }${c0} Download and launch a cantboot script to break the current system Designed for and tested on RHEL 5, 6, and 7 Options: ${cB}--ls${c0} Fetch a list of available TOKENs ${cB}--ll${c0} Fetch a list of available TOKENs + descriptions ${cB}--lesson${c0} TOKEN Fetch details about a TOKEN ${cB}--update${c0} Update this launcher script ${cY}Please note: the sole purpose of this app is to BREAK YOUR SYSTEM and make it UNBOOTABLE. Use at your own risk, preferably on a virtual machine.${c0} Version info: ${version:2} See to contribute" } # If no args ... if [[ ${#} -eq 0 ]]; then USAGE exit fi # Case through all args case ${1} in -h|--help) USAGE exit ;; --ls) curl -LsSk ${gitRoot}/tokens-ls exit ;; --ll) curl -LsSk ${gitRoot}/tokens-ll exit ;; --lesson) if curl -LsSk ${gitRoot}/lessons/"${2}"; then echo exit else RED "Problem connecting to github (no internet?)" exit 1 fi ;; --update) do_update=True ;; -*) RED "Invalid option" exit 1 ;; esac # Check a few things if [[ ${#} -gt 1 ]]; then RED "Invalid number of arguments" USAGE exit 1 elif [[ $(id -u) != 0 ]]; then RED "Must be run as root" exit 1 fi # Create sub tempdir in /dev/shm (tons of bash constructs use TMPDIR) [[ -d /dev/shm && -w /dev/shm ]] && parent=/dev/shm || parent=/tmp export parent TMPDIR=$(mktemp -d -p ${parent}) # Remove temp dir when we're done trap "rm -rf ${TMPDIR} 2>/dev/null" EXIT SIGINT SIGTERM # Ensure anything we create goes to TMPDIR cd ${TMPDIR} if [[ ${do_update} == True ]]; then UPDATE fi # These might be needed for REBOOT -f cp -a /bin/{sleep,sync} . # We should increment this any time we add new functions cantbootVersionMagic=2 FAIL() { [[ -n ${1} ]] && failText=${@} RED "Aborting: There was a problem setting up an essential part of this break script" [[ -n ${failText} ]] && BOLD "${failText}" exit 1 } SORRY() { [[ -n ${1} ]] && sorryText=${@} YELLOW "Aborting: Sorry -- this break script ${sorryText}" exit 1 } REQUIRE_CANTBOOT_VERSION_MAGIC() { if [[ ${1} -gt ${cantbootVersionMagic} ]]; then YELLOW "Sorry -- this break script requires a newer cantboot breakboot version" printf "${cB}Update now? [y]/n${c0} " read -ep "" if [[ ${REPLY} != n ]]; then UPDATE fi fi } REQUIRE_RHEL_EQUAL_TO() { : } REQUIRE_RHEL_GREATER_THAN() { : } REQUIRE_RHEL_LESS_THAN() { : } REQUIRE_RHEL_VERSION_NUMBERS() { : } REQUIRE_SELINUX_CFGFILE() { # CANTBOOT_VERSION_MAGIC 1 sorryText="requires root-writable SELinux cfgfile /etc/selinux/config" [[ -e /etc/selinux/config ]] || SORRY touch /etc/selinux/config 2>/dev/null || SORRY } # CANTBOOT_VERSION_MAGIC 1 possible_grub_cfgfile_locations=" /boot/grub/grub.conf /boot/efi/EFI/redhat/grub.conf /boot/grub2/grub.cfg /boot/efi/EFI/redhat/grub.cfg /boot/grub/grub.cfg " REQUIRE_GRUB1() { # CANTBOOT_VERSION_MAGIC 1 sorryText="requires GRUB v1 system" for file in /boot/grub/{grub.conf,stage1,stage2}; do [[ -f ${file} ]] || SORRY done } REQUIRE_GRUB_MBR() { # CANTBOOT_VERSION_MAGIC 1 sorryText="requires GRUB v1 or v2 embedded in system MBR" mbrbootdisk=$(awk '$2=="/boot" {sub(/[0-9]+$/, "", $1); print $1}' /proc/mounts) [[ -b ${mbrbootdisk} ]] || SORRY echo $(dd if=${mbrbootdisk} count=1 2>/dev/null | strings) | grep -qs 'GRUB.*Geom.*Hard.*Disk.*Read.*Error' || SORRY } REQUIRE_GRUB2() { # CANTBOOT_VERSION_MAGIC 1 sorryText="requires GRUB v2 system" command -v grub2-install >/dev/null || SORRY } REQUIRE_LVM_SWAP() { # CANTBOOT_VERSION_MAGIC 1 sorryText="requires a swap device on top of LVM" for swapdev in $(awk '$3=="swap" {if ($1 !~ /^#/) print $1}' /etc/fstab); do unset LVM2_VG_NAME LVM2_LV_NAME LVM2_LV_PATH case ${swapdev} in UUID=*) eval ${swapdev} swapdev=/dev/disk/by-uuid/${UUID} ;; LABEL=*) eval ${swapdev} swapdev=/dev/disk/by-label/${LABEL} ;; /dev/*) : ;; *) continue esac lvs --nameprefixes --noheadings -o lv_name ${swapdev} &>/dev/null || continue eval $(lvs --nameprefixes --noheadings -o vg_name,lv_name,lv_path ${swapdev}) eval $(blkid ${swapdev} | cut -d: -f2) return done SORRY } REQUIRE_LVM_ROOTFS() { # CANTBOOT_VERSION_MAGIC 1 sorryText="requires rootfs on top of LVM" rootdev=$(mount 2>/dev/null | awk '$3=="/" {if ($1 ~ /^\/dev/) print $1}') [[ -n ${rootdev} ]] || SORRY lvs ${rootdev} &>/dev/null || SORRY [[ ${rootdev::11} == /dev/mapper ]] && rootdev=$(lvs --noheadings -o lv_path ${rootdev} | tr -d ' ') } REQUIRE_LVM_SINGLE_ROOT_PV_PARTITION() { # CANTBOOT_VERSION_MAGIC 2 REQUIRE_LVM_ROOTFS sorryText="requires a single contiguous LVM physical volume partition backing the root LV" pvCount=$(lvs --noheadings -o +devices ${rootdev} | wc -l) [[ ${pvCount} == 1 ]] || SORRY rootPV=$(lvs --noheadings -o devices ${rootdev} | sed -e 's,\s,,g' -e 's,(.*),,') # This check right here isn't perfect, but it will suffice for the vast majority of cases grep -qs '[0-9]$' <<<"${rootPV}" || SORRY } REQUIRE_DRACUT_INITRAMFS() { # CANTBOOT_VERSION_MAGIC 1 sorryText="requires kernel using modern initramfs files" if ! command -v dracut >/dev/null || [[ ! -f /boot/initramfs-$(uname -r).img ]]; then SORRY fi } REQUIRE_MKINITRD_INITRD() { # CANTBOOT_VERSION_MAGIC 1 sorryText="requires kernel using old-style initrd files" if ! command -v mkinitrd >/dev/null || [[ ! -f /boot/initrd-$(uname -r).img ]]; then SORRY fi } REBOOT() { # CANTBOOT_VERSION_MAGIC 1 echo "All done Reboot time" if [[ ${1} == -f ]]; then ${TMPDIR}/sync for sysrq in u b; do echo ${sysrq} >/proc/sysrq-trigger ${TMPDIR}/sleep 5 done else reboot fi } cyan "Inspecting TOKEN and attempting download ..." if curl -LsSko breakscript ${gitRoot}/breaks/"${1}"; then if file breakscript | grep -qs 'Bourne-Again shell script'; then green "Break script payload downloaded intact; executing ..." source breakscript else RED "Problem downloading break script; received file not as expected" read -ep "Press Ctrl-c to quit or Enter to see received file " less -S breakscript exit 1 fi else RED "Problem downloading break script; curl command failed" echo -e "Try executing ${cB}${0##*/} --list${c0}" exit 1 fi