cryptinit.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461
  1. /*
  2. * cryptinit 1.0.2 - setup encrypted root/swap system using LUKS
  3. *
  4. * Copyright (C) 2009 Waldemar Brodkorb <mail@waldemar-brodkorb.de>
  5. * Copyright (C) 2008 Phil Sutter <phil@nwl.cc>
  6. *
  7. * This program is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU General Public License
  9. * as published by the Free Software Foundation; either version 2
  10. * of the License, or (at your option) any later version.
  11. *
  12. * This program is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. * GNU General Public License for more details.
  16. *
  17. * strongly based on ideas and work from Phil Sutter
  18. * http://nwl.cc/cgi-bin/git/gitweb.cgi?p=initramfs-init.git;a=summary
  19. * - used with cryptsetup 1.0.6 (needs a small cryptsetup-patch)
  20. * - see comment at the end of file for a useful initramfs filelist
  21. * - compile and link with following commands to get a static init
  22. * gcc -Wall -c -o init.o cryptinit.c
  23. * libtool --mode=link --tag=CC gcc -all-static -o init init.o \
  24. * /usr/lib/libcryptsetup.la
  25. */
  26. #include <errno.h>
  27. #include <fcntl.h>
  28. #include <stdarg.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <unistd.h>
  33. #include <libcryptsetup.h>
  34. #include <sys/mount.h>
  35. #include <sys/reboot.h>
  36. #include <sys/types.h>
  37. #include <sys/utsname.h>
  38. #include <sys/wait.h>
  39. #define HOSTNAME "linux"
  40. #define DOMAINNAME "foo.bar"
  41. #define CRYPT_SWAP_DEV "/dev/sda3"
  42. #define CRYPT_SWAP_NAME "swap"
  43. #define CRYPT_ROOT_DEV "/dev/sda2"
  44. #define CRYPT_ROOT_NAME "root"
  45. #define PROCPATH "/proc"
  46. #define SYSPATH "/sys"
  47. #define PROCFS "proc"
  48. #define SYSFS "sysfs"
  49. #define DEF_KERN_CONS "/dev/console"
  50. #define DEF_KERN_SWAP "/dev/mapper/swap"
  51. #define DEF_KERN_ROOT_SRC "/dev/mapper/root"
  52. #define DEF_KERN_ROOT_TGT "/mnt"
  53. #define DEF_KERN_ROOT_FS "xfs"
  54. #define DEF_KERN_INIT "/init"
  55. #ifndef MS_MOVE
  56. #define MS_MOVE 8192
  57. #endif
  58. /* a structure for holding options to mount() a device */
  59. struct mntopts {
  60. char *source;
  61. char *target;
  62. char *fstype;
  63. unsigned long flags;
  64. };
  65. /* a structure for holding kernel boot parameters */
  66. struct commandline {
  67. struct mntopts root;
  68. char *init;
  69. char *resume;
  70. ushort do_resume;
  71. ushort debug;
  72. };
  73. struct commandline cmdline;
  74. void debug_printf(const char *format, ...) {
  75. va_list params;
  76. if(cmdline.debug) {
  77. va_start(params, format);
  78. vprintf(format, params);
  79. va_end(params);
  80. }
  81. }
  82. void debug_msg(const char *s) {
  83. if(cmdline.debug)
  84. fputs(s, stderr);
  85. }
  86. void log_msg(const char *s) {
  87. fputs(s, stdout);
  88. }
  89. /* logging function from cryptsetup library */
  90. static void cmdLineLog(int class, char *msg) {
  91. switch(class) {
  92. case CRYPT_LOG_NORMAL:
  93. debug_msg(msg);
  94. break;
  95. case CRYPT_LOG_ERROR:
  96. debug_msg(msg);
  97. break;
  98. default:
  99. fprintf(stderr, "Internal error for msg: %s", msg);
  100. break;
  101. }
  102. }
  103. int switch_root(char *console, char *newroot, char *init) {
  104. if (chdir(newroot)) {
  105. fprintf(stderr,"bad newroot %s\n",newroot);
  106. return 1;
  107. }
  108. /* Overmount / with newdir and chroot into it. The chdir is needed to
  109. * recalculate "." and ".." links. */
  110. if (mount(".", "/", NULL, MS_MOVE, NULL) || chroot(".") || chdir("/")) {
  111. fprintf(stderr,"switch_root: error moving root\n");
  112. return 2;
  113. }
  114. /* If a new console specified, redirect stdin/stdout/stderr to that. */
  115. if (console) {
  116. close(0);
  117. if(open(console, O_RDWR) < 0) {
  118. fprintf(stderr,"Bad console '%s'\n",console);
  119. return 4;
  120. }
  121. dup2(0, 1);
  122. dup2(0, 2);
  123. }
  124. log_msg("Starting Linux from encrypted root disk\n");
  125. /* Exec real init. (This is why we must be pid 1.) */
  126. execl(init, init, (char *)NULL);
  127. fprintf(stderr,"Bad init '%s'\n",init);
  128. return 3;
  129. }
  130. char *read_cmdline(void) {
  131. FILE *fp;
  132. int linelen, i;
  133. char *str;
  134. if((fp=fopen("/proc/cmdline","r")) == NULL) {
  135. perror("fopen()");
  136. return NULL;
  137. }
  138. linelen = 10;
  139. str = calloc(linelen, sizeof(char));
  140. for(i=0;(str[i]=fgetc(fp)) != EOF; i++) {
  141. if(i>linelen-1) {
  142. linelen += 10;
  143. if((str=realloc(str, linelen)) == NULL) {
  144. perror("realloc()");
  145. return NULL;
  146. }
  147. }
  148. }
  149. str[i-1] = '\0'; /* substitutes \n for \0 */
  150. fclose(fp);
  151. return str;
  152. }
  153. int parse_cmdline(char *line) {
  154. int tmpnum;
  155. char *tmpstr, *lstr, *rstr, *idx;
  156. char *invchars[1];
  157. tmpstr = strtok(line, " ");
  158. do {
  159. if((idx=strchr(tmpstr, '=')) != NULL) {
  160. rstr = idx + 1;
  161. idx = '\0';
  162. lstr = tmpstr;
  163. if(!strncmp(lstr, "rootfstype", 10)) {
  164. cmdline.root.fstype = rstr;
  165. } else if(!strncmp(lstr, "root", 4)) {
  166. cmdline.root.source = rstr;
  167. } else if(!strncmp(lstr, "init", 4)) {
  168. cmdline.init = rstr;
  169. } else if(!strncmp(lstr, "resume", 6)) {
  170. cmdline.resume = rstr;
  171. }
  172. } else if(!strncmp(tmpstr, "noresume", 8)) {
  173. cmdline.do_resume = 0;
  174. } else if(!strncmp(tmpstr, "debug", 5)) {
  175. cmdline.debug=1;
  176. } else {
  177. if(cmdline.debug)
  178. printf("unknown bootparam flag %s\n",tmpstr);
  179. }
  180. } while((tmpstr = strtok(NULL, " ")) != NULL);
  181. debug_printf("\n Bootparams scanned:\n");
  182. debug_printf("root\t%s\nrootfstype\t%s\ninit\t%s\nresume\t%s\ndo_resume\t%i\n",
  183. cmdline.root.source,cmdline.root.fstype,cmdline.init,cmdline.resume,cmdline.do_resume);
  184. debug_printf("debug\t%i\n\n",
  185. cmdline.debug);
  186. return 0;
  187. }
  188. int get_cmdline() {
  189. char *str;
  190. /* first set some useful defaults */
  191. cmdline.root.source = DEF_KERN_ROOT_SRC;
  192. cmdline.root.target = DEF_KERN_ROOT_TGT;
  193. cmdline.root.fstype = DEF_KERN_ROOT_FS;
  194. cmdline.root.flags = MS_RDONLY;
  195. cmdline.init = DEF_KERN_INIT;
  196. cmdline.resume = DEF_KERN_SWAP;
  197. cmdline.do_resume = 1;
  198. cmdline.debug = 0;
  199. /* read out cmdline from /proc */
  200. str = read_cmdline();
  201. /* parse the cmdline */
  202. if(parse_cmdline(str))
  203. return -1;
  204. return 0;
  205. }
  206. void kmsg_log(int level) {
  207. FILE *fd;
  208. debug_msg("Finetune kernel log\n");
  209. if((fd = fopen("/proc/sys/kernel/printk", "r+")) == NULL) {
  210. perror("fopen()");
  211. return;
  212. }
  213. fprintf(fd, "%d", level);
  214. fclose(fd);
  215. }
  216. void do_resume(void) {
  217. FILE *fd;
  218. debug_msg("Trying to resume\n");
  219. if((fd = fopen("/sys/power/resume", "a")) == NULL) {
  220. return;
  221. }
  222. fprintf(fd, "254:0\n");
  223. fclose(fd);
  224. }
  225. void do_halt(void) {
  226. int pid;
  227. /* run sync just to be sure */
  228. sync();
  229. /* fork to prevent a kernel panic while killing init */
  230. if((pid=fork()) == 0) {
  231. reboot(0x4321fedc);
  232. _exit(0);
  233. }
  234. waitpid(pid, NULL, 0);
  235. }
  236. int do_mount(struct mntopts o) {
  237. debug_printf("do_mount: mounting %s with fstype %s\n", o.source, o.fstype);
  238. if(mount(o.source, o.target, o.fstype, o.flags, NULL)) {
  239. perror("mount()");
  240. debug_printf("do_mount: mounting %s with fstype %s\n failed", o.source, o.fstype);
  241. return errno;
  242. }
  243. return 0;
  244. }
  245. int main(void) {
  246. char errormsg[100];
  247. int i;
  248. int wrongpass;
  249. char *pass;
  250. struct utsname info;
  251. int ret;
  252. const char hostname[20] = HOSTNAME;
  253. const char domainname[20] = DOMAINNAME;
  254. struct crypt_options options;
  255. struct interface_callbacks cmd_icb;
  256. struct mntopts mopts[2] = {
  257. { "proc", PROCPATH, PROCFS, 0 },
  258. { "sysfs", SYSPATH, SYSFS, 0 }
  259. };
  260. /* need to set callback functions, log is required */
  261. cmd_icb.yesDialog = NULL;
  262. cmd_icb.log = cmdLineLog;
  263. /* first try to mount needed virtual filesystems */
  264. if(do_mount(mopts[0]) || do_mount(mopts[1])) {
  265. fprintf(stderr, "Error mounting %s and %s\n",
  266. PROCPATH, SYSPATH);
  267. exit(errno);
  268. }
  269. /* get kernel command line */
  270. if(get_cmdline() == -1) {
  271. fprintf(stderr, "Failed to parse kernel commandline\n");
  272. exit(errno);
  273. }
  274. /* keep kernel quiet while asking for password */
  275. kmsg_log(0);
  276. /* first unlock swap partition for resume */
  277. memset(&options, 0, sizeof(struct crypt_options));
  278. options.name = CRYPT_SWAP_NAME;
  279. options.device = CRYPT_SWAP_DEV;
  280. options.icb = &cmd_icb;
  281. ret = uname(&info);
  282. if (ret < 0)
  283. fprintf(stderr, "Error calling uname function\n");
  284. /* security by obscurity */
  285. printf("This is %s.%s (Linux %s %s)\n", hostname, domainname, info.machine, info.release);
  286. printf("%s login: ", hostname);
  287. fflush(stdout);
  288. while(getchar() != '\n');
  289. /* unlock swap */
  290. debug_msg("Unlocking Swap\n");
  291. for(i=0; i<3; i++) {
  292. /* ask user for password */
  293. if((pass=getpass("Password: ")) == NULL) {
  294. perror("getpass()");
  295. return errno;
  296. }
  297. options.passphrase = pass;
  298. /* try to unlock swap */
  299. if((wrongpass=crypt_luksOpen(&options))) {
  300. printf("Login incorrect\n");
  301. crypt_get_error(errormsg, 99);
  302. debug_printf("Error: %s\n", errormsg);
  303. } else { /* success */
  304. if(i > 0)
  305. fprintf(stderr, "%i incorrect attempts\n",i);
  306. break;
  307. }
  308. }
  309. if(wrongpass) {
  310. fprintf(stderr, "Panic - you are not allowed!\n");
  311. sleep(3);
  312. do_halt();
  313. }
  314. /* try to resume here */
  315. if(cmdline.do_resume) {
  316. debug_msg("Trying to resume from swap\n");
  317. do_resume();
  318. debug_msg("Resume failed, starting normal boot\n");
  319. }
  320. /* resume returned, starting normal boot */
  321. options.name = CRYPT_ROOT_NAME;
  322. options.device = CRYPT_ROOT_DEV;
  323. /* unlock root device */
  324. debug_msg("Unlocking Root\n");
  325. if(crypt_luksOpen(&options)) {
  326. perror("crypt_luksOpen()");
  327. crypt_get_error(errormsg, 99);
  328. debug_printf("Error: %s\n", errormsg);
  329. }
  330. /* mount root filesystem */
  331. if(do_mount(cmdline.root)) {
  332. puts("Error mounting root");
  333. exit(errno);
  334. }
  335. kmsg_log(6);
  336. /* no need for /sys anymore */
  337. debug_msg("Unmounting /sys\n");
  338. if(umount("/sys"))
  339. perror("umount()");
  340. /* no need for /proc anymore */
  341. debug_msg("Unmounting /proc\n");
  342. if(umount("/proc"))
  343. perror("umount()");
  344. /* remove password from RAM */
  345. memset(pass, 0, strlen(pass)*sizeof(char));
  346. debug_msg("Switching root\n");
  347. switch_root(DEF_KERN_CONS, cmdline.root.target, cmdline.init);
  348. return(0);
  349. }
  350. /*
  351. example initramfs file list:
  352. dir /dev 755 0 0
  353. dir /dev/mapper 755 0 0
  354. dir /proc 755 0 0
  355. dir /sys 755 0 0
  356. dir /mnt 755 0 0
  357. nod /dev/console 644 0 0 c 5 1
  358. nod /dev/tty 660 0 0 c 5 0
  359. nod /dev/tty0 600 0 0 c 4 0
  360. nod /dev/sda 644 0 0 b 8 0
  361. nod /dev/sda1 644 0 0 b 8 1
  362. nod /dev/sda2 644 0 0 b 8 2
  363. nod /dev/sda3 644 0 0 b 8 3
  364. nod /dev/sda4 644 0 0 b 8 4
  365. nod /dev/null 644 0 0 c 1 3
  366. nod /dev/mapper/control 644 0 0 c 10 62
  367. nod /dev/urandom 644 0 0 c 1 9
  368. file /init /usr/src/init 755 0 0
  369. cryptsetup patch:
  370. Index: lib/setup.c
  371. ===================================================================
  372. --- lib/setup.c (revision 40)
  373. +++ lib/setup.c (working copy)
  374. @@ -538,10 +538,17 @@
  375. start:
  376. mk=NULL;
  377. - if(get_key("Enter LUKS passphrase: ",&password,&passwordLen, 0, options->key_file, options->passphrase_fd, options->timeout, options->flags))
  378. - tries--;
  379. - else
  380. - tries = 0;
  381. + if(options->passphrase) {
  382. + password = NULL;
  383. + password = safe_alloc(512);
  384. + strcpy(password, options->passphrase);
  385. + passwordLen = strlen(password);
  386. + } else {
  387. + if(get_key("Enter LUKS passphrase: ",&password,&passwordLen, 0, options->key_file, options->passphrase_fd, options->timeout, options->flags))
  388. + tries--;
  389. + else
  390. + tries = 0;
  391. + }
  392. if(!password) {
  393. r = -EINVAL; goto out;
  394. */