summaryrefslogtreecommitdiff
path: root/package/cryptinit/src/cryptinit.c
diff options
context:
space:
mode:
Diffstat (limited to 'package/cryptinit/src/cryptinit.c')
-rw-r--r--package/cryptinit/src/cryptinit.c469
1 files changed, 469 insertions, 0 deletions
diff --git a/package/cryptinit/src/cryptinit.c b/package/cryptinit/src/cryptinit.c
new file mode 100644
index 000000000..b0d570846
--- /dev/null
+++ b/package/cryptinit/src/cryptinit.c
@@ -0,0 +1,469 @@
+/*
+ * cryptinit 1.0.2 - setup encrypted root/swap system using LUKS
+ *
+ * Copyright (C) 2009 Waldemar Brodkorb <mail@waldemar-brodkorb.de>
+ * Copyright (C) 2008 Phil Sutter <phil@nwl.cc>
+ *
+ * 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 2
+ * 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.
+ *
+ * strongly based on ideas and work from Phil Sutter
+ * http://nwl.cc/cgi-bin/git/gitweb.cgi?p=initramfs-init.git;a=summary
+ * - used with cryptsetup 1.0.6 (needs a small cryptsetup-patch)
+ * - see comment at the end of file for a useful initramfs filelist
+ * - compile and link with following commands to get a static init
+ * gcc -Wall -c -o init.o cryptinit.c
+ * libtool --mode=link --tag=CC gcc -all-static -o init init.o \
+ * /usr/lib/libcryptsetup.la
+ */
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <libcryptsetup.h>
+#include <sys/mount.h>
+#include <sys/reboot.h>
+#include <sys/types.h>
+#include <sys/utsname.h>
+#include <sys/wait.h>
+
+#define HOSTNAME "linux"
+#define DOMAINNAME "foo.bar"
+
+#define CRYPT_SWAP_DEV "/dev/sda3"
+#define CRYPT_SWAP_NAME "swap"
+#define CRYPT_ROOT_DEV "/dev/sda2"
+#define CRYPT_ROOT_NAME "root"
+
+#define PROCPATH "/proc"
+#define SYSPATH "/sys"
+#define PROCFS "proc"
+#define SYSFS "sysfs"
+
+#define DEF_KERN_CONS "/dev/console"
+#define DEF_KERN_SWAP "/dev/mapper/swap"
+#define DEF_KERN_ROOT_SRC "/dev/mapper/root"
+#define DEF_KERN_ROOT_TGT "/mnt"
+#define DEF_KERN_ROOT_FS "xfs"
+#define DEF_KERN_INIT "/start"
+#define DEF_KERN_RUNLEVEL "3"
+
+#ifndef MS_MOVE
+#define MS_MOVE 8192
+#endif
+
+/* a structure for holding options to mount() a device */
+struct mntopts {
+ char *source;
+ char *target;
+ char *fstype;
+ unsigned long flags;
+};
+
+/* a structure for holding kernel boot parameters */
+struct commandline {
+ struct mntopts root;
+ char *init;
+ char *resume;
+ char *runlevel;
+ ushort do_resume;
+ ushort debug;
+};
+
+struct commandline cmdline;
+
+void debug_printf(const char *format, ...) {
+ va_list params;
+ if(cmdline.debug) {
+ va_start(params, format);
+ vprintf(format, params);
+ va_end(params);
+ }
+}
+
+void debug_msg(const char *s) {
+ if(cmdline.debug)
+ fputs(s, stderr);
+}
+
+void log_msg(const char *s) {
+ fputs(s, stdout);
+}
+
+/* logging function from cryptsetup library */
+static void cmdLineLog(int class, char *msg) {
+ switch(class) {
+ case CRYPT_LOG_NORMAL:
+ debug_msg(msg);
+ break;
+ case CRYPT_LOG_ERROR:
+ debug_msg(msg);
+ break;
+ default:
+ fprintf(stderr, "Internal error for msg: %s", msg);
+ break;
+ }
+}
+
+int switch_root(char *console, char *newroot, char *init, char *initarg) {
+
+ if (chdir(newroot)) {
+ fprintf(stderr,"bad newroot %s\n",newroot);
+ return 1;
+ }
+ /* Overmount / with newdir and chroot into it. The chdir is needed to
+ * recalculate "." and ".." links. */
+ if (mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".") || chdir("/")) {
+ fprintf(stderr,"switch_root: error moving root\n");
+ return 2;
+ }
+
+ /* If a new console specified, redirect stdin/stdout/stderr to that. */
+ if (console) {
+ close(0);
+ if(open(console, O_RDWR) < 0) {
+ fprintf(stderr,"Bad console '%s'\n",console);
+ return 4;
+ }
+ dup2(0, 1);
+ dup2(0, 2);
+ }
+
+ log_msg("Starting Linux from encrypted root disk\n");
+ /* Exec real init. (This is why we must be pid 1.) */
+ execl(init, init, (char *)NULL);
+ fprintf(stderr,"Bad init '%s'\n",init);
+ return 3;
+}
+
+char *read_cmdline(void) {
+ FILE *fp;
+ int linelen, i;
+ char *str;
+
+ if((fp=fopen("/proc/cmdline","r")) == NULL) {
+ perror("fopen()");
+ return NULL;
+ }
+ linelen = 10;
+ str = calloc(linelen, sizeof(char));
+ for(i=0;(str[i]=fgetc(fp)) != EOF; i++) {
+ if(i>linelen-1) {
+ linelen += 10;
+ if((str=realloc(str, linelen)) == NULL) {
+ perror("realloc()");
+ return NULL;
+ }
+ }
+ }
+ str[i-1] = '\0'; /* substitutes \n for \0 */
+ fclose(fp);
+ return str;
+}
+
+int parse_cmdline(char *line) {
+ int tmpnum;
+ char *tmpstr, *lstr, *rstr, *idx;
+ char *invchars[1];
+
+ tmpstr = strtok(line, " ");
+ do {
+ if((idx=strchr(tmpstr, '=')) != NULL) {
+ rstr = idx + 1;
+ idx = '\0';
+ lstr = tmpstr;
+
+ if(!strncmp(lstr, "rootfstype", 10)) {
+ cmdline.root.fstype = rstr;
+
+ } else if(!strncmp(lstr, "root", 4)) {
+ cmdline.root.source = rstr;
+
+ } else if(!strncmp(lstr, "init", 4)) {
+ cmdline.init = rstr;
+
+ } else if(!strncmp(lstr, "resume", 6)) {
+ cmdline.resume = rstr;
+ }
+
+ } else if(!strncmp(tmpstr, "noresume", 8)) {
+ cmdline.do_resume = 0;
+
+ } else if(!strncmp(tmpstr, "debug", 5)) {
+ cmdline.debug=1;
+
+ } else if(strlen(tmpstr) == 1) {
+ tmpnum = (int)strtol(tmpstr, invchars, 10);
+ if(**invchars == '\0' && tmpnum >= 0) {
+ cmdline.runlevel = tmpstr;
+ }
+ } else {
+ if(cmdline.debug)
+ printf("unknown bootparam flag %s\n",tmpstr);
+ }
+ } while((tmpstr = strtok(NULL, " ")) != NULL);
+
+ debug_printf("\n Bootparams scanned:\n");
+ debug_printf("root\t%s\nrootfstype\t%s\ninit\t%s\nresume\t%s\ndo_resume\t%i\n",
+ cmdline.root.source,cmdline.root.fstype,cmdline.init,cmdline.resume,cmdline.do_resume);
+ debug_printf("debug\t%i\nrunlevel\t%s\n\n",
+ cmdline.debug,cmdline.runlevel);
+ return 0;
+}
+
+int get_cmdline() {
+ char *str;
+
+ /* first set some useful defaults */
+ cmdline.root.source = DEF_KERN_ROOT_SRC;
+ cmdline.root.target = DEF_KERN_ROOT_TGT;
+ cmdline.root.fstype = DEF_KERN_ROOT_FS;
+ cmdline.root.flags = MS_RDONLY;
+ cmdline.init = DEF_KERN_INIT;
+ cmdline.resume = DEF_KERN_SWAP;
+ cmdline.do_resume = 1;
+ cmdline.debug = 0;
+ cmdline.runlevel = DEF_KERN_RUNLEVEL;
+
+ /* read out cmdline from /proc */
+ str = read_cmdline();
+
+ /* parse the cmdline */
+ if(parse_cmdline(str))
+ return -1;
+
+ return 0;
+}
+
+void kmsg_log(int level) {
+ FILE *fd;
+
+ debug_msg("Finetune kernel log\n");
+ if((fd = fopen("/proc/sys/kernel/printk", "r+")) == NULL) {
+ perror("fopen()");
+ return;
+ }
+ fprintf(fd, "%d", level);
+ fclose(fd);
+}
+
+void do_resume(void) {
+ FILE *fd;
+
+ debug_msg("Running tuxonice-resume\n");
+ if((fd = fopen("/sys/power/tuxonice/do_resume", "a")) == NULL) {
+ return;
+ }
+ fprintf(fd, "1\n");
+ fclose(fd);
+}
+
+void do_halt(void) {
+ int pid;
+
+ /* run sync just to be sure */
+ sync();
+
+ /* fork to prevent a kernel panic while killing init */
+ if((pid=fork()) == 0) {
+ reboot(0x4321fedc);
+ _exit(0);
+ }
+ waitpid(pid, NULL, 0);
+}
+
+int do_mount(struct mntopts o) {
+ debug_printf("do_mount: mounting %s with fstype %s\n", o.source, o.fstype);
+ if(mount(o.source, o.target, o.fstype, o.flags, NULL)) {
+ perror("mount()");
+ debug_printf("do_mount: mounting %s with fstype %s\n failed", o.source, o.fstype);
+ return errno;
+ }
+ return 0;
+}
+
+int main(void) {
+ char errormsg[100];
+ int i;
+ int wrongpass;
+ char *pass;
+ struct utsname info;
+ int ret;
+ const char hostname[20] = HOSTNAME;
+ const char domainname[20] = DOMAINNAME;
+ struct crypt_options options;
+ struct interface_callbacks cmd_icb;
+
+ struct mntopts mopts[2] = {
+ { "proc", PROCPATH, PROCFS, 0 },
+ { "sysfs", SYSPATH, SYSFS, 0 }
+ };
+
+ /* need to set callback functions, log is required */
+ cmd_icb.yesDialog = NULL;
+ cmd_icb.log = cmdLineLog;
+
+ /* first try to mount needed virtual filesystems */
+ if(do_mount(mopts[0]) || do_mount(mopts[1])) {
+ fprintf(stderr, "Error mounting %s and %s\n",
+ PROCPATH, SYSPATH);
+ exit(errno);
+ }
+
+ /* get kernel command line */
+ if(get_cmdline() == -1) {
+ fprintf(stderr, "Failed to parse kernel commandline\n");
+ exit(errno);
+ }
+
+ /* keep kernel quiet while asking for password */
+ kmsg_log(0);
+
+ /* first unlock swap partition for resume */
+ memset(&options, 0, sizeof(struct crypt_options));
+ options.name = CRYPT_SWAP_NAME;
+ options.device = CRYPT_SWAP_DEV;
+ options.icb = &cmd_icb;
+
+ ret = uname(&info);
+ if (ret < 0)
+ fprintf(stderr, "Error calling uname\n");
+
+ /* security by obscurity */
+ printf("This is %s.%s (Linux %s %s)\n", hostname, domainname, info.machine, info.release);
+ printf("%s login: ", hostname);
+ fflush(stdout);
+ while(getchar() != '\n');
+ /* unlock swap */
+ debug_msg("Unlocking Swap\n");
+ for(i=0; i<3; i++) {
+ /* ask user for password */
+ if((pass=getpass("Password: ")) == NULL) {
+ perror("getpass()");
+ return errno;
+ }
+ options.passphrase = pass;
+ /* try to unlock swap */
+ if((wrongpass=crypt_luksOpen(&options))) {
+ printf("Login incorrect\n");
+ crypt_get_error(errormsg, 99);
+ debug_printf("Error: %s\n", errormsg);
+ } else { /* success */
+ if(i > 0)
+ fprintf(stderr, "%i incorrect attempts\n",i);
+ break;
+ }
+ }
+
+ if(wrongpass) {
+ fprintf(stderr, "Panic - you are not allowed!\n");
+ sleep(3);
+ do_halt();
+ }
+
+ /* try to resume here */
+ if(cmdline.do_resume) {
+ debug_msg("Trying to resume from swap\n");
+ do_resume();
+ debug_msg("Resume failed, starting normal boot\n");
+ }
+
+ /* resume returned, starting normal boot */
+ options.name = CRYPT_ROOT_NAME;
+ options.device = CRYPT_ROOT_DEV;
+
+ /* unlock root device */
+ debug_msg("Unlocking Root\n");
+ if(crypt_luksOpen(&options)) {
+ perror("crypt_luksOpen()");
+ crypt_get_error(errormsg, 99);
+ debug_printf("Error: %s\n", errormsg);
+ }
+
+ /* mount root filesystem */
+ if(do_mount(cmdline.root)) {
+ puts("Error mounting root");
+ exit(errno);
+ }
+
+ kmsg_log(6);
+
+ /* no need for /sys anymore */
+ debug_msg("Unmounting /sys\n");
+ if(umount("/sys"))
+ perror("umount()");
+
+ /* no need for /proc anymore */
+ debug_msg("Unmounting /proc\n");
+ if(umount("/proc"))
+ perror("umount()");
+
+ /* remove password from RAM */
+ memset(pass, 0, strlen(pass)*sizeof(char));
+
+ debug_msg("Switching root\n");
+ switch_root(DEF_KERN_CONS, cmdline.root.target, cmdline.init, cmdline.runlevel);
+
+ return(0);
+}
+/*
+example initramfs file list:
+
+dir /dev 755 0 0
+dir /dev/mapper 755 0 0
+dir /proc 755 0 0
+dir /sys 755 0 0
+dir /mnt 755 0 0
+nod /dev/console 644 0 0 c 5 1
+nod /dev/tty 660 0 0 c 5 0
+nod /dev/tty0 600 0 0 c 4 0
+nod /dev/sda 644 0 0 b 8 0
+nod /dev/sda1 644 0 0 b 8 1
+nod /dev/sda2 644 0 0 b 8 2
+nod /dev/sda3 644 0 0 b 8 3
+nod /dev/sda4 644 0 0 b 8 4
+nod /dev/null 644 0 0 c 1 3
+nod /dev/mapper/control 644 0 0 c 10 62
+nod /dev/urandom 644 0 0 c 1 9
+file /init /usr/src/init 755 0 0
+
+cryptsetup patch:
+
+Index: lib/setup.c
+===================================================================
+--- lib/setup.c (revision 40)
++++ lib/setup.c (working copy)
+@@ -538,10 +538,17 @@
+ start:
+ mk=NULL;
+
+- if(get_key("Enter LUKS passphrase: ",&password,&passwordLen, 0, options->key_file, options->passphrase_fd, options->timeout, options->flags))
+- tries--;
+- else
+- tries = 0;
++ if(options->passphrase) {
++ password = NULL;
++ password = safe_alloc(512);
++ strcpy(password, options->passphrase);
++ passwordLen = strlen(password);
++ } else {
++ if(get_key("Enter LUKS passphrase: ",&password,&passwordLen, 0, options->key_file, options->passphrase_fd, options->timeout, options->flags))
++ tries--;
++ else
++ tries = 0;
++ }
+
+ if(!password) {
+ r = -EINVAL; goto out;
+*/