clevis-luks-udisks2.c 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676
  1. /* vim: set tabstop=8 shiftwidth=4 softtabstop=4 expandtab smarttab colorcolumn=80: */
  2. /*
  3. * Copyright (c) 2015 Red Hat, Inc.
  4. * Author: Nathaniel McCallum <npmccallum@redhat.com>
  5. *
  6. * This program is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. #include <udisks/udisks.h>
  20. #include <glib-unix.h>
  21. #include <luksmeta.h>
  22. #include <jansson.h>
  23. #include <sys/types.h>
  24. #include <sys/socket.h>
  25. #include <sys/wait.h>
  26. #include <string.h>
  27. #include <stdbool.h>
  28. #include <stdint.h>
  29. #include <stdio.h>
  30. #include <libaudit.h>
  31. #include <getopt.h>
  32. #include <pwd.h>
  33. #include <grp.h>
  34. #define MAX_UDP 65507
  35. #define UERR ((uid_t) -1)
  36. #define GERR ((gid_t) -1)
  37. #define UUID_TMPL \
  38. "%02hhx%02hhx%02hhx%02hhx-" \
  39. "%02hhx%02hhx-%02hhx%02hhx-%02hhx%02hhx-" \
  40. "%02hhx%02hhx%02hhx%02hhx%02hhx%02hhx"
  41. #define UUID_ARGS(u) \
  42. u[0x0], u[0x1], u[0x2], u[0x3], u[0x4], u[0x5], u[0x6], u[0x7], \
  43. u[0x8], u[0x9], u[0xa], u[0xb], u[0xc], u[0xd], u[0xe], u[0xf]
  44. typedef struct {
  45. ssize_t used;
  46. char data[MAX_UDP];
  47. } pkt_t;
  48. enum {
  49. PIPE_RD = 0,
  50. PIPE_WR = 1
  51. };
  52. struct context {
  53. UDisksClient *clt;
  54. GMainLoop *loop;
  55. GList *lst;
  56. int sock;
  57. };
  58. static const luksmeta_uuid_t CLEVIS_LUKS_UUID = {
  59. 0xcb, 0x6e, 0x89, 0x04, 0x81, 0xff, 0x40, 0xda,
  60. 0xa8, 0x4a, 0x07, 0xab, 0x9a, 0xb5, 0x71, 0x5e
  61. };
  62. static void
  63. remove_path(GList **lst, const char *path)
  64. {
  65. GList *i = NULL;
  66. while ((i = g_list_find_custom(*lst, path, (GCompareFunc) g_strcmp0))) {
  67. *lst = g_list_remove(*lst, i->data);
  68. g_free(i->data);
  69. }
  70. }
  71. static gboolean
  72. idle(gpointer misc)
  73. {
  74. struct context *ctx = misc;
  75. GVariant *options = NULL;
  76. options = g_variant_new_parsed("@a{sv} { %s: <true> }",
  77. "auth.no_user_interaction");
  78. if (!options)
  79. goto error;
  80. g_variant_ref_sink(options);
  81. for (GList *i = ctx->lst; i; i = i->next) {
  82. UDisksEncrypted *enc = NULL;
  83. const char *path = i->data;
  84. UDisksObject *uobj = NULL;
  85. UDisksBlock *block = NULL;
  86. const char *dev = NULL;
  87. pkt_t pkt = {};
  88. uobj = udisks_client_peek_object(ctx->clt, path);
  89. if (!uobj)
  90. continue;
  91. enc = udisks_object_peek_encrypted(uobj);
  92. if (!enc)
  93. continue;
  94. block = udisks_object_peek_block(uobj);
  95. if (!block)
  96. continue;
  97. dev = udisks_block_get_device(block);
  98. if (!dev)
  99. continue;
  100. pkt.used = strlen(dev) + 1;
  101. if ((size_t) pkt.used > sizeof(pkt.data))
  102. continue;
  103. strcpy(pkt.data, dev);
  104. if (send(ctx->sock, pkt.data, pkt.used, 0) != pkt.used) {
  105. g_main_loop_quit(ctx->loop);
  106. break;
  107. }
  108. memset(&pkt, 0, sizeof(pkt));
  109. pkt.used = recv(ctx->sock, pkt.data, sizeof(pkt.data), 0);
  110. if (pkt.used == 0)
  111. continue;
  112. else if (pkt.used < 0 || (size_t) pkt.used >= sizeof(pkt.data)) {
  113. g_main_loop_quit(ctx->loop);
  114. break;
  115. }
  116. /* NOTE: pkt.data is now implicitly NULL terminated regardless of
  117. * whether or not the plaintext inside the JWE was terminated. */
  118. udisks_encrypted_call_unlock_sync(enc, pkt.data, options,
  119. NULL, NULL, NULL);
  120. memset(&pkt, 0, sizeof(pkt));
  121. }
  122. error:
  123. g_list_free_full(ctx->lst, g_free);
  124. g_variant_unref(options);
  125. ctx->lst = NULL;
  126. return FALSE;
  127. }
  128. static void
  129. oadd(GDBusObjectManager *mgr, GDBusObject *obj, gpointer misc)
  130. {
  131. struct context *ctx = misc;
  132. UDisksObject *uobj = NULL;
  133. const char *path = NULL;
  134. const char *back = NULL;
  135. UDisksBlock *ct = NULL;
  136. UDisksBlock *pt = NULL;
  137. GList *tmp = NULL;
  138. char *ptmp = NULL;
  139. path = g_dbus_object_get_object_path(obj);
  140. if (!path)
  141. return;
  142. uobj = udisks_client_peek_object(ctx->clt, path);
  143. if (!uobj)
  144. return;
  145. ct = udisks_object_peek_block(uobj);
  146. if (!ct)
  147. return;
  148. back = udisks_block_get_crypto_backing_device(ct);
  149. if (back)
  150. remove_path(&ctx->lst, back);
  151. if (!udisks_block_get_hint_auto(ct))
  152. return;
  153. if (!udisks_object_peek_encrypted(uobj))
  154. return;
  155. pt = udisks_client_get_cleartext_block(ctx->clt, ct);
  156. if (pt) {
  157. g_object_unref(pt);
  158. return;
  159. }
  160. ptmp = g_strdup(path);
  161. if (!ptmp)
  162. return;
  163. tmp = g_list_prepend(ctx->lst, ptmp);
  164. if (!tmp) {
  165. g_free(ptmp);
  166. return;
  167. }
  168. ctx->lst = tmp;
  169. g_idle_add(idle, ctx);
  170. }
  171. static void
  172. orem(GDBusObjectManager *mgr, GDBusObject *obj, gpointer misc)
  173. {
  174. struct context *ctx = misc;
  175. remove_path(&ctx->lst, g_dbus_object_get_object_path(obj));
  176. }
  177. static gboolean
  178. sockerr(gint fd, GIOCondition cond, gpointer misc)
  179. {
  180. struct context *ctx = misc;
  181. close(fd);
  182. g_main_loop_quit(ctx->loop);
  183. return FALSE;
  184. }
  185. static int
  186. child_main(int sock)
  187. {
  188. struct context ctx = { .sock = sock };
  189. int exit_status = EXIT_FAILURE;
  190. GDBusObjectManager *mgr = NULL;
  191. gulong id = 0;
  192. ctx.loop = g_main_loop_new(NULL, FALSE);
  193. if (!ctx.loop)
  194. goto error;
  195. ctx.clt = udisks_client_new_sync(NULL, NULL);
  196. if (!ctx.clt)
  197. goto error;
  198. mgr = udisks_client_get_object_manager(ctx.clt);
  199. if (!mgr)
  200. goto error;
  201. id = g_signal_connect(mgr, "object-added", G_CALLBACK(oadd), &ctx);
  202. if (id == 0)
  203. goto error;
  204. id = g_signal_connect(mgr, "object-removed", G_CALLBACK(orem), &ctx);
  205. if (id == 0)
  206. goto error;
  207. id = g_unix_fd_add(sock, G_IO_ERR, sockerr, &ctx);
  208. if (id == 0)
  209. goto error;
  210. g_main_loop_run(ctx.loop);
  211. exit_status = EXIT_SUCCESS;
  212. error:
  213. g_list_free_full(ctx.lst, g_free);
  214. g_main_loop_unref(ctx.loop);
  215. g_object_unref(ctx.clt);
  216. close(sock);
  217. return exit_status;
  218. }
  219. /*
  220. * ==========================================================================
  221. * Caution, code below this point runs with euid = 0!
  222. * ==========================================================================
  223. */
  224. static int pair[2] = { -1, -1 };
  225. pid_t pid = 0;
  226. static void
  227. safeclose(int *fd)
  228. {
  229. if (*fd >= 0)
  230. close(*fd);
  231. *fd = -1;
  232. }
  233. static void
  234. on_signal(int sig)
  235. {
  236. if (sig == SIGCHLD) {
  237. if (wait(NULL) != pid)
  238. return;
  239. pid = -1;
  240. }
  241. safeclose(&pair[0]);
  242. }
  243. static ssize_t
  244. recover_key(const pkt_t *jwe, char *out, size_t max, uid_t uid, gid_t gid)
  245. {
  246. int push[2] = { -1, -1 };
  247. int pull[2] = { -1, -1 };
  248. ssize_t bytes = 0;
  249. pid_t chld = 0;
  250. if (pipe(push) != 0)
  251. goto error;
  252. if (pipe(pull) != 0)
  253. goto error;
  254. chld = fork();
  255. if (chld < 0) {
  256. perror("fork");
  257. goto error;
  258. }
  259. if (chld == 0) {
  260. char *const env[] = { "PATH=" BINDIR, NULL };
  261. int r = 0;
  262. if (geteuid() != 0) {
  263. if (setgroups(1, &gid) != 0) {
  264. perror("setgroups");
  265. /* Can fail if missing permissions */
  266. }
  267. }
  268. if (setgid(gid) != 0) {
  269. perror("setgid");
  270. exit(EXIT_FAILURE);
  271. }
  272. if (setuid(uid) != 0) {
  273. perror("setuid");
  274. exit(EXIT_FAILURE);
  275. }
  276. r = dup2(push[PIPE_RD], STDIN_FILENO);
  277. if (r != STDIN_FILENO) {
  278. perror("dup2");
  279. exit(EXIT_FAILURE);
  280. }
  281. r = dup2(pull[PIPE_WR], STDOUT_FILENO);
  282. if (r != STDOUT_FILENO) {
  283. perror("dup2");
  284. exit(EXIT_FAILURE);
  285. }
  286. safeclose(&push[PIPE_RD]);
  287. safeclose(&push[PIPE_WR]);
  288. safeclose(&pull[PIPE_RD]);
  289. safeclose(&pull[PIPE_WR]);
  290. execle(BINDIR "/clevis", "clevis", "decrypt", NULL, env);
  291. perror("execle");
  292. exit(EXIT_FAILURE);
  293. }
  294. safeclose(&push[PIPE_RD]);
  295. safeclose(&pull[PIPE_WR]);
  296. bytes = write(push[PIPE_WR], jwe->data, jwe->used);
  297. safeclose(&push[PIPE_WR]);
  298. if (bytes < 0 || bytes != jwe->used) {
  299. errno = errno == 0 ? EIO : errno;
  300. kill(chld, SIGTERM);
  301. goto error;
  302. }
  303. bytes = 0;
  304. for (ssize_t block = 1; block > 0; bytes += block) {
  305. block = read(pull[PIPE_RD], &out[bytes], max - bytes);
  306. if (block < 0) {
  307. kill(chld, SIGTERM);
  308. goto error;
  309. }
  310. }
  311. safeclose(&pull[PIPE_RD]);
  312. return bytes;
  313. error:
  314. safeclose(&push[PIPE_RD]);
  315. safeclose(&push[PIPE_WR]);
  316. safeclose(&pull[PIPE_RD]);
  317. safeclose(&pull[PIPE_WR]);
  318. return -errno;
  319. }
  320. static bool
  321. log_attempt(int log, struct crypt_device *cd, bool success)
  322. {
  323. const char *uuid = NULL;
  324. char msg[4096] = {};
  325. char *dev = NULL;
  326. int r = 0;
  327. uuid = crypt_get_uuid(cd);
  328. if (!uuid)
  329. return false;
  330. dev = audit_encode_nv_string("device", crypt_get_device_name(cd), 0);
  331. if (!dev)
  332. return false;
  333. r = snprintf(msg, sizeof(msg),
  334. "op=recovered-key-for uuid=%s %s",
  335. uuid, dev);
  336. free(dev);
  337. if (r < 0 || r == sizeof(msg))
  338. return false;
  339. return audit_log_user_message(log, AUDIT_USER_DEVICE, msg,
  340. NULL, NULL, NULL, success) > 0;
  341. }
  342. static const char *sopts = "hu:g:";
  343. static const struct option lopts[] = {
  344. { "help", no_argument, .val = 'h' },
  345. { "user", required_argument, .val = 'u' },
  346. { "group", required_argument, .val = 'g' },
  347. {}
  348. };
  349. static uid_t
  350. usr2uid(const char *usr)
  351. {
  352. const struct passwd *tmp = getpwnam(usr);
  353. return tmp ? tmp->pw_uid : UERR;
  354. }
  355. static gid_t
  356. grp2gid(const char *grp)
  357. {
  358. const struct group *tmp = getgrnam(grp);
  359. return tmp ? tmp->gr_gid : GERR;
  360. }
  361. static bool
  362. token_to_jwe(const char *json, pkt_t *pkt)
  363. {
  364. json_auto_t *tokn = NULL;
  365. const json_t *jwe = NULL;
  366. const char *prt = NULL;
  367. const char *key = NULL;
  368. const char *tag = NULL;
  369. const char *iv = NULL;
  370. const char *ct = NULL;
  371. tokn = json_loads(json, 0, NULL);
  372. if (!tokn)
  373. return false;
  374. jwe = json_object_get(tokn, "jwe");
  375. if (!jwe)
  376. return false;
  377. if (json_unpack((json_t *) jwe, "{s:s,s:s,s:s,s:s,s:s}",
  378. "protected", &prt, "encrypted_key", &key, "iv", &iv,
  379. "ciphertext", &ct, "tag", &tag) < 0)
  380. return false;
  381. pkt->used = snprintf(pkt->data, sizeof(pkt->data),
  382. "%s.%s.%s.%s.%s", prt, key, iv, ct, tag);
  383. if (pkt->used < 0 || (size_t) pkt->used > sizeof(pkt->data))
  384. return false;
  385. pkt->used--; /* Remove null terminator. */
  386. return true;
  387. }
  388. int
  389. main(int argc, char *const argv[])
  390. {
  391. gid_t recg = grp2gid(CLEVIS_GROUP); /* Recovery group */
  392. uid_t recu = usr2uid(CLEVIS_USER); /* Recovery user */
  393. gid_t unlg = getgid(); /* Unlock group */
  394. uid_t unlu = getuid(); /* Unlock user */
  395. int log = -1;
  396. if (recu == UERR) {
  397. fprintf(stderr, "Invalid user name '%s'!\n", CLEVIS_USER);
  398. return EXIT_FAILURE;
  399. }
  400. if (recg == GERR) {
  401. fprintf(stderr, "Invalid group name '%s'!\n", CLEVIS_GROUP);
  402. return EXIT_FAILURE;
  403. }
  404. if (geteuid() != 0) {
  405. fprintf(stderr, "Root privileges required!\n");
  406. return EXIT_FAILURE;
  407. }
  408. for (int c; (c = getopt_long(argc, argv, sopts, lopts, NULL)) >= 0; ) {
  409. switch (c) {
  410. case 'u':
  411. if (getuid() != 0) {
  412. fprintf(stderr, "You can only specify the user as root!\n");
  413. return EXIT_FAILURE;
  414. }
  415. unlu = usr2uid(optarg);
  416. if (unlu == 0 || unlu == UERR) {
  417. fprintf(stderr, "Invalid user name '%s'!\n", optarg);
  418. return EXIT_FAILURE;
  419. }
  420. break;
  421. case 'g':
  422. if (getuid() != 0) {
  423. fprintf(stderr, "You can only specify the group as root!\n");
  424. return EXIT_FAILURE;
  425. }
  426. unlg = grp2gid(optarg);
  427. if (unlg == 0 || unlg == GERR) {
  428. fprintf(stderr, "Invalid group name '%s'!\n", optarg);
  429. return EXIT_FAILURE;
  430. }
  431. break;
  432. default:
  433. fprintf(stderr, "Usage: clevis-luks-udisks2 [-u USER -g GROUP]\n");
  434. return EXIT_FAILURE;
  435. }
  436. }
  437. if (unlu == 0 || unlg == 0) {
  438. fprintf(stderr, "Either run as SETUID=root or use -u/-g!\n");
  439. return EXIT_FAILURE;
  440. }
  441. if (socketpair(AF_UNIX, SOCK_DGRAM, 0, pair) == -1)
  442. return EXIT_FAILURE;
  443. pid = fork();
  444. if (pid < 0) {
  445. safeclose(&pair[0]);
  446. safeclose(&pair[1]);
  447. return EXIT_FAILURE;
  448. }
  449. if (pid == 0) {
  450. int status = EXIT_FAILURE;
  451. safeclose(&pair[0]);
  452. if (setgid(unlg) == 0 && setegid(unlg) == 0 &&
  453. setuid(unlu) == 0 && seteuid(unlu) == 0)
  454. status = child_main(pair[1]);
  455. safeclose(&pair[1]);
  456. return status;
  457. }
  458. safeclose(&pair[1]);
  459. signal(SIGHUP, on_signal);
  460. signal(SIGINT, on_signal);
  461. signal(SIGPIPE, on_signal);
  462. signal(SIGTERM, on_signal);
  463. signal(SIGUSR1, on_signal);
  464. signal(SIGUSR2, on_signal);
  465. signal(SIGCHLD, on_signal);
  466. if (setgid(0) == -1 || setegid(0) == -1 ||
  467. setuid(0) == -1 || seteuid(0) == -1)
  468. goto error;
  469. log = audit_open();
  470. if (log < 0)
  471. goto error;
  472. for (pkt_t req = {}, jwe = {}, key = {}; ; key = (pkt_t) {}) {
  473. struct crypt_device *cd = NULL;
  474. /* Receive a request. Ensure that it is null terminated. */
  475. req.used = recv(pair[0], req.data, sizeof(req.data), 0);
  476. if (req.used < 1 || req.data[req.used - 1])
  477. break;
  478. if (crypt_init(&cd, req.data) < 0)
  479. goto next;
  480. if (crypt_load(cd, CRYPT_LUKS1, NULL) >= 0) {
  481. const int slotlen = crypt_keyslot_max(CRYPT_LUKS1);
  482. luksmeta_uuid_t uuid = {};
  483. for (uint8_t s = 0; s < slotlen && key.used <= 0; s++) {
  484. fprintf(stderr, "%s\tSLOT\t%hhu\n", req.data, s);
  485. switch (crypt_keyslot_status(cd, s)) {
  486. case CRYPT_SLOT_ACTIVE:
  487. case CRYPT_SLOT_ACTIVE_LAST:
  488. break;
  489. default:
  490. continue;
  491. }
  492. jwe.used = luksmeta_load(cd, s, uuid, jwe.data, sizeof(jwe.data));
  493. fprintf(stderr, "%s\tMETA\t%s\n",
  494. req.data, strerror(jwe.used < 0 ? -jwe.used : 0));
  495. if (jwe.used <= 0)
  496. continue;
  497. fprintf(stderr, "%s\tUUID\t" UUID_TMPL "\n",
  498. req.data, UUID_ARGS(uuid));
  499. if (memcmp(uuid, CLEVIS_LUKS_UUID, sizeof(uuid)) != 0)
  500. continue;
  501. /* Recover the key from the JWE. */
  502. key.used = recover_key(&jwe, key.data, sizeof(key.data), recu, recg);
  503. fprintf(stderr, "%s\tRCVR\t%s (%zd)\n", req.data,
  504. strerror(key.used < 0 ? -key.used : 0), key.used);
  505. }
  506. } else if (crypt_load(cd, CRYPT_LUKS2, NULL) >= 0) {
  507. for (int t = 0; key.used <= 0; t++) {
  508. const char *json = NULL;
  509. const char *type = NULL;
  510. int r = 0;
  511. r = crypt_token_status(cd, t, &type);
  512. if (r == CRYPT_TOKEN_INVALID)
  513. break;
  514. else if (r != CRYPT_TOKEN_EXTERNAL_UNKNOWN)
  515. continue;
  516. fprintf(stderr, "%s\tTOKN\t%d\t%s\n", req.data, t, type);
  517. if (strcmp(type, "clevis") != 0)
  518. continue;
  519. r = crypt_token_json_get(cd, t, &json);
  520. fprintf(stderr, "%s\tMETA\t%s\n",
  521. req.data, strerror(r < 0 ? -r : 0));
  522. if (!token_to_jwe(json, &jwe))
  523. continue;
  524. /* Recover the key from the JWE. */
  525. key.used = recover_key(&jwe, key.data, sizeof(key.data), recu, recg);
  526. fprintf(stderr, "%s\tRCVR\t%s (%zd)\n", req.data,
  527. strerror(key.used < 0 ? -key.used : 0), key.used);
  528. }
  529. }
  530. if (key.used < 0)
  531. key.used = 0;
  532. /* Don't return the key unless auditing succeeds. */
  533. if (!log_attempt(log, cd, key.used > 0))
  534. memset(&key, 0, sizeof(key));
  535. next:
  536. crypt_free(cd);
  537. /* Send the key as a reply. */
  538. if (send(pair[0], key.data, key.used, 0) != key.used)
  539. break;
  540. }
  541. error:
  542. safeclose(&log);
  543. safeclose(&pair[0]);
  544. if (pid != -1) {
  545. kill(pid, SIGTERM);
  546. waitpid(pid, NULL, 0);
  547. }
  548. return EXIT_FAILURE;
  549. }