Skip to content

Instantly share code, notes, and snippets.

@jyio
Created April 22, 2012 03:30
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jyio/2446834 to your computer and use it in GitHub Desktop.
Save jyio/2446834 to your computer and use it in GitHub Desktop.
PengDroid: a healthy fusion of Debian and Android
#!/bin/sh
set -e -x
export PATH=$PATH:/usr/local/sbin:/usr/sbin:/sbin
mkdir -p /usr/share/man/man1
/var/lib/dpkg/info/dash.preinst install || true
dpkg --configure -a
ln -sf update-rc.d-insserv /usr/sbin/update-rc.d
dpkg --clear-selections
dpkg --set-selections < /selections
apt-get -u dselect-upgrade --purge
ln -sf update-rc.d-insserv /usr/sbin/update-rc.d
ln -sf /bin/sh /bin/bash
echo BotBrew > /etc/debian_chroot
mkdir /android
ln -s /android/system /system
ln -s /android/data /data
ln -s /android/cache /cache
rm /config.sh
rm /selections
rm /usr/bin/qemu-arm-static
#include <libgen.h>
#include <getopt.h>
#include <stdio.h>
#include <stdlib.h>
#include <alloca.h>
#include <malloc.h>
#include <unistd.h>
#include <sched.h>
#include <errno.h>
#include <sys/stat.h>
#include <sys/mount.h>
#include <sys/types.h>
#include <sys/wait.h>
#include "strnstr.h"
#define ENV_PATH "/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games:/usr/libexec/busybox"
struct config {
char *target;
char *cwd;
char *const *argv;
};
struct mountspec {
char *src;
char *dst;
char *type;
unsigned long flags;
void *data;
unsigned long remount_flags;
};
static struct mountspec foreign_mounts[] = {
{NULL,"/proc","proc",0,NULL,0},
{"/dev","/dev",NULL,MS_BIND,NULL,0},
{"/dev/pts","/dev/pts",NULL,MS_BIND,NULL,0},
{NULL,"/sys","sysfs",0,NULL,0},
{NULL,"/android","tmpfs",MS_NODEV|MS_NOEXEC|MS_NOATIME,"size=1M,mode=0755",0},
{"/cache","/android/cache",NULL,MS_BIND,NULL,0},
{"/data","/android/data",NULL,MS_BIND,NULL,0},
{"/emmc","/android/emmc",NULL,MS_BIND,NULL,0},
{"/sd-ext","/android/sd-ext",NULL,MS_BIND,NULL,0},
{"/sdcard","/android/sdcard",NULL,MS_BIND,NULL,0},
{"/system","/android/system",NULL,MS_BIND,NULL,MS_REMOUNT|MS_NODEV|MS_NOATIME},
{"/usbdisk","/android/usbdisk",NULL,MS_BIND,NULL,0},
{"/system/xbin","/android/system/xbin",NULL,MS_BIND,NULL,MS_REMOUNT|MS_NODEV|MS_NOATIME}
};
static int n_foreign_mounts = sizeof(foreign_mounts)/sizeof(foreign_mounts[0]);
static void usage(char *progname) {
fprintf(stderr,
"Usage: %s [options] [--] [<command>...]\n"
"\n"
"Available options:\n"
"\t-d <directory>\t| --dir=<directory>\tSpecify chroot directory\n",
progname);
exit(EXIT_FAILURE);
}
static void privdrop(void) {
gid_t gid = getgid();
uid_t uid = getuid();
setgroups(1,&gid);
#ifdef linux
setregid(gid,gid);
setreuid(uid,uid);
#else
setegid(gid);
setgid(gid);
seteuid(uid);
setuid(uid);
#endif
}
static int main_clone(struct config *config) {
char *initsh = alloca(snprintf(NULL,0,"%s/init.sh",config->target)+1);
sprintf(initsh,"%s/init.sh",config->target);
char cwd[PATH_MAX];
if(getcwd(cwd,sizeof(cwd)) == NULL) {
fprintf(stderr,"whoops: cannot get working directory\n");
return EXIT_FAILURE;
}
if(chdir(config->target)) {
fprintf(stderr,"whoops: cannot chdir to namespace\n");
return EXIT_FAILURE;
}
size_t target_len = strlen(config->target);
int i;
for(i = 0; i < n_foreign_mounts; i++) {
struct mountspec m = foreign_mounts[i];
size_t dst_len = strlen(m.dst);
char *dst = malloc(target_len+dst_len+1);
memcpy(dst,config->target,target_len);
memcpy(dst+target_len,m.dst,dst_len+1); // includes null terminator
mkdir(dst,0755);
if(mount(m.src,dst,m.type,m.flags,m.data)) rmdir(dst);
else if(m.remount_flags) mount(dst,dst,NULL,m.remount_flags|MS_REMOUNT,NULL);
free(dst);
}
if(chroot(".")) {
fprintf(stderr,"whoops: cannot chroot\n");
return EXIT_FAILURE;
}
if((chdir(cwd))&&(chdir("/"))) {
fprintf(stderr,"whoops: cannot chdir to chroot\n");
return EXIT_FAILURE;
}
privdrop();
char *env_path = getenv("PATH");
if((env_path)&&(env_path[0])) {
size_t env_path_len = strlen(env_path);
char *newpath = malloc(sizeof(ENV_PATH)+env_path_len);
char *append = strstr(env_path,"::");
if(append) { // break string at :: and insert
append++;
size_t prepend_len = append-env_path;
memcpy(newpath,env_path,prepend_len);
memcpy(newpath+prepend_len,ENV_PATH,sizeof(ENV_PATH)-1);
memcpy(newpath+prepend_len+sizeof(ENV_PATH)-1,append,env_path_len-prepend_len+1);
} else {
memcpy(newpath,ENV_PATH":",sizeof(ENV_PATH":")-1);
memcpy(newpath+sizeof(ENV_PATH":")-1,env_path,env_path_len+1); // includes null terminator
}
setenv("PATH",newpath,1);
free(newpath);
} else setenv("PATH",ENV_PATH":/android/sbin:/system/sbin:/system/bin:/system/xbin",1);
if(config->argv == NULL) {
char *argv0[2];
argv0[0] = "/init.sh";
argv0[1] = 0;
config->argv = (char**)&argv0;
}
if(execvp(config->argv[0],config->argv)) {
int i = 1;
fprintf(stderr,"whoops: cannot run `%s",config->argv[0]);
while(config->argv[i]) fprintf(stderr," %s",config->argv[i++]);
fprintf(stderr,"'\n");
return errno;
}
return 0; // wtf
}
int main(int argc, char *argv[]) {
struct config config;
struct stat st;
char apath[PATH_MAX];
// get absolute path
config.target = realpath(dirname(argv[0]),apath);
uid_t uid = getuid();
int c;
while(1) {
static struct option long_options[] = {
{"dir",required_argument,0,'d'},
{0,0,0,0}
};
int option_index = 0;
c = getopt_long(argc,argv,"d:",long_options,&option_index);
if(c == -1) break;
switch(c) {
case 'd':
// prevent privilege escalation: only superuser can chroot to arbitrary directories
if(uid) {
fprintf(stderr,"whoops: --dir is only available for uid=0\n");
return EXIT_FAILURE;
}
config.target = realpath(optarg,apath);
break;
default:
usage(argv[0]);
}
}
config.argv = (optind==argc)?NULL:(argv+optind);
// prevent privilege escalation: fail if link/symlink is not owned by superuser
if(uid) {
if(lstat(argv[0],&st)) {
fprintf(stderr,"whoops: cannot stat `%s'\n",argv[0]);
return EXIT_FAILURE;
}
if(st.st_uid) {
fprintf(stderr,"whoops: `%s' is not owned by uid=0\n",argv[0]);
return EXIT_FAILURE;
}
}
// check if directory exists
if((stat(config.target,&st))||(!S_ISDIR(st.st_mode))) {
fprintf(stderr,"whoops: `%s' is not a directory\n",config.target);
return EXIT_FAILURE;
}
if((st.st_uid)||(st.st_gid)) chown(config.target,0,0);
if((st.st_mode&S_IWGRP)||(st.st_mode&S_IWOTH)) chmod(config.target,0755);
// check if directory mounted
FILE *fp;
char *haystack;
size_t len;
size_t target_len = strlen(config.target);
char *needle = malloc(target_len+3);
needle[0] = needle[target_len+1] = ' ';
memcpy(needle+1,config.target,target_len+1); // includes null terminator
int mounted = 0;
if(fp = fopen("/proc/self/mounts","r")) while(haystack = fgetln(fp,&len)) if(strnstr(haystack,needle,len)) {
mounted = 1;
break;
}
free(needle);
if(!mounted) {
if(geteuid()) {
fprintf(stderr,"whoops: superuser privileges required for first invocation of `%s'\n",argv[0]);
return EXIT_FAILURE;
}
mount(config.target,config.target,NULL,MS_BIND,NULL);
mount(config.target,config.target,NULL,MS_REMOUNT|MS_NODEV|MS_NOATIME,NULL);
if(!stat(argv[0],&st)) {
// setuid
if((st.st_uid)||(st.st_gid)) chown(argv[0],0,0);
if((st.st_mode&S_IWGRP)||(st.st_mode&S_IWOTH)||!(st.st_mode&S_ISUID)) chmod(argv[0],04755);
}
}
// clone with new namespace
long stacksz = sysconf(_SC_PAGESIZE);
void *stack = alloca(stacksz)+stacksz;
pid_t pid = clone(main_clone,stack,SIGCHLD|CLONE_NEWNS|CLONE_FILES,(void*)&config);
if(pid < 0) {
fprintf(stderr,"whoops: cannot clone\n");
return EXIT_FAILURE;
} else {
int ret;
privdrop();
while((waitpid(pid,&ret,0)<0)&&(errno == EINTR));
return WIFEXITED(ret)?WEXITSTATUS(ret):EXIT_FAILURE;
}
}
#!/system/bin/sh
case "$0" in
/init.sh)
exec /system/bin/sh
;;
esac
[General]
arch = armel
directory = ./pengdroid/
cleanup = true
noauth = true
unpack = true
debootstrap = Grip
aptsources = Grip
setupscript= ./setup.sh
configscript= ./config.sh
[Grip]
keyring = emdebian-archive-keyring
source = http://www.emdebian.org/grip
suite = squeeze
apt install
coreutils install
dash install
debconf install
debconf-i18n install
debian-archive-keyring install
debianutils install
dpkg install
emdebian-archive-keyring install
findutils install
gcc-4.4-base install
gnupg install
gpgv install
insserv install
libacl1 install
libattr1 install
libbz2-1.0 install
libc-bin install
libc6 install
libdb4.8 install
libgcc1 install
liblocale-gettext-perl install
liblzma2 install
libncurses5 install
libpam-modules install
libpam-runtime install
libpam0g install
libreadline6 install
libselinux1 install
libstdc++6 install
libtext-charwidth-perl install
libtext-iconv-perl install
libtext-wrapi18n-perl install
libusb-0.1-4 install
mawk install
passwd install
perl-base install
readline-common install
sed install
sensible-utils install
xz-utils install
zlib1g install
#!/bin/sh
set -e -x
# pass path to the root. Don't let it run without one as it will break your system
if [ "" = "$1" ] ; then
echo "You need to specify a path to the target rootfs"
else
if [ -e "$1" ] ; then
ROOTFS="$1"
else
echo "Root dir $ROOTFS not found"
fi
fi
if [ "/" = "ROOTFS" ] ; then echo "Refusing to change your build system's files" ; fi
# set hostname
echo botbrew > $ROOTFS/etc/hostname
echo "127.0.0.1 localhost.localdomain localhost" > ${ROOTFS}/etc/hosts
echo "127.0.1.1 botbrew botbrew" >> ${ROOTFS}/etc/hosts
echo "nameserver 8.8.8.8" > ${ROOTFS}/etc/resolv.conf
echo "nameserver 8.8.4.4" >> ${ROOTFS}/etc/resolv.conf
cp /usr/bin/qemu-arm-static ${ROOTFS}/usr/bin/
cp selections ${ROOTFS}/
cp init.sh ${ROOTFS}/
/*-
* Copyright (c) 2001 Mike Barcroft <mike@FreeBSD.org>
* Copyright (c) 1990, 1993
* The Regents of the University of California. All rights reserved.
*
* This code is derived from software contributed to Berkeley by
* Chris Torek.
*
* 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.
* 3. All advertising materials mentioning features or use of this software
* must display the following acknowledgement:
* This product includes software developed by the University of
* California, Berkeley and its contributors.
* 4. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS 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 REGENTS 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.
*/
#if defined(LIBC_SCCS) && !defined(lint)
static char sccsid[] = "@(#)strstr.c 8.1 (Berkeley) 6/4/93";
#endif /* LIBC_SCCS and not lint */
#include <sys/cdefs.h>
__FBSDID("$FreeBSD: src/lib/libc/string/strnstr.c,v 1.2.2.1 2001/12/09 06:50:03 mike Exp $");
#include <string.h>
/*
* Find the first occurrence of find in s, where the search is limited to the
* first slen characters of s.
*/
char *
strnstr(s, find, slen)
const char *s;
const char *find;
size_t slen;
{
char c, sc;
size_t len;
if ((c = *find++) != '\0') {
len = strlen(find);
do {
do {
if ((sc = *s++) == '\0' || slen-- < 1)
return (NULL);
} while (sc != c);
if (len > slen)
return (NULL);
} while (strncmp(s, find, len) != 0);
s--;
}
return ((char *)s);
}
#ifndef STRNSTR_H
#define STRNSTR_H
char *strnstr(const char *s, const char *find, size_t slen);
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment