ssh-sk-helper.c 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. /* $OpenBSD: ssh-sk-helper.c,v 1.10 2020/05/26 01:59:46 djm Exp $ */
  2. /*
  3. * Copyright (c) 2019 Google LLC
  4. *
  5. * Permission to use, copy, modify, and distribute this software for any
  6. * purpose with or without fee is hereby granted, provided that the above
  7. * copyright notice and this permission notice appear in all copies.
  8. *
  9. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  10. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  11. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
  12. * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  13. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
  14. * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  15. * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  16. */
  17. /*
  18. * This is a tiny program used to isolate the address space used for
  19. * security key middleware signing operations from ssh-agent. It is similar
  20. * to ssh-pkcs11-helper.c but considerably simpler as the operations for
  21. * security keys are stateless.
  22. *
  23. * Please crank SSH_SK_HELPER_VERSION in sshkey.h for any incompatible
  24. * protocol changes.
  25. */
  26. #include "includes.h"
  27. #include <limits.h>
  28. #include <stdarg.h>
  29. #include <stdio.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <unistd.h>
  33. #include <errno.h>
  34. #include "xmalloc.h"
  35. #include "log.h"
  36. #include "sshkey.h"
  37. #include "authfd.h"
  38. #include "misc.h"
  39. #include "sshbuf.h"
  40. #include "msg.h"
  41. #include "uidswap.h"
  42. #include "sshkey.h"
  43. #include "ssherr.h"
  44. #include "ssh-sk.h"
  45. #ifdef ENABLE_SK
  46. extern char *__progname;
  47. static struct sshbuf *reply_error(int r, char *fmt, ...)
  48. __attribute__((__format__ (printf, 2, 3)));
  49. static struct sshbuf *
  50. reply_error(int r, char *fmt, ...)
  51. {
  52. char *msg;
  53. va_list ap;
  54. struct sshbuf *resp;
  55. va_start(ap, fmt);
  56. xvasprintf(&msg, fmt, ap);
  57. va_end(ap);
  58. debug("%s: %s", __progname, msg);
  59. free(msg);
  60. if (r >= 0)
  61. fatal("%s: invalid error code %d", __func__, r);
  62. if ((resp = sshbuf_new()) == NULL)
  63. fatal("%s: sshbuf_new failed", __progname);
  64. if (sshbuf_put_u32(resp, SSH_SK_HELPER_ERROR) != 0 ||
  65. sshbuf_put_u32(resp, (u_int)-r) != 0)
  66. fatal("%s: buffer error", __progname);
  67. return resp;
  68. }
  69. /* If the specified string is zero length, then free it and replace with NULL */
  70. static void
  71. null_empty(char **s)
  72. {
  73. if (s == NULL || *s == NULL || **s != '\0')
  74. return;
  75. free(*s);
  76. *s = NULL;
  77. }
  78. static struct sshbuf *
  79. process_sign(struct sshbuf *req)
  80. {
  81. int r = SSH_ERR_INTERNAL_ERROR;
  82. struct sshbuf *resp, *kbuf;
  83. struct sshkey *key = NULL;
  84. uint32_t compat;
  85. const u_char *message;
  86. u_char *sig = NULL;
  87. size_t msglen, siglen = 0;
  88. char *provider = NULL, *pin = NULL;
  89. if ((r = sshbuf_froms(req, &kbuf)) != 0 ||
  90. (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 ||
  91. (r = sshbuf_get_string_direct(req, &message, &msglen)) != 0 ||
  92. (r = sshbuf_get_cstring(req, NULL, NULL)) != 0 || /* alg */
  93. (r = sshbuf_get_u32(req, &compat)) != 0 ||
  94. (r = sshbuf_get_cstring(req, &pin, NULL)) != 0)
  95. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  96. if (sshbuf_len(req) != 0)
  97. fatal("%s: trailing data in request", __progname);
  98. if ((r = sshkey_private_deserialize(kbuf, &key)) != 0)
  99. fatal("Unable to parse private key: %s", ssh_err(r));
  100. if (!sshkey_is_sk(key))
  101. fatal("Unsupported key type %s", sshkey_ssh_name(key));
  102. debug("%s: ready to sign with key %s, provider %s: "
  103. "msg len %zu, compat 0x%lx", __progname, sshkey_type(key),
  104. provider, msglen, (u_long)compat);
  105. null_empty(&pin);
  106. if ((r = sshsk_sign(provider, key, &sig, &siglen,
  107. message, msglen, compat, pin)) != 0) {
  108. resp = reply_error(r, "Signing failed: %s", ssh_err(r));
  109. goto out;
  110. }
  111. if ((resp = sshbuf_new()) == NULL)
  112. fatal("%s: sshbuf_new failed", __progname);
  113. if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_SIGN)) != 0 ||
  114. (r = sshbuf_put_string(resp, sig, siglen)) != 0)
  115. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  116. out:
  117. sshkey_free(key);
  118. sshbuf_free(kbuf);
  119. free(provider);
  120. if (sig != NULL)
  121. freezero(sig, siglen);
  122. if (pin != NULL)
  123. freezero(pin, strlen(pin));
  124. return resp;
  125. }
  126. static struct sshbuf *
  127. process_enroll(struct sshbuf *req)
  128. {
  129. int r;
  130. u_int type;
  131. char *provider, *application, *pin, *device, *userid;
  132. uint8_t flags;
  133. struct sshbuf *challenge, *attest, *kbuf, *resp;
  134. struct sshkey *key;
  135. if ((attest = sshbuf_new()) == NULL ||
  136. (kbuf = sshbuf_new()) == NULL)
  137. fatal("%s: sshbuf_new failed", __progname);
  138. if ((r = sshbuf_get_u32(req, &type)) != 0 ||
  139. (r = sshbuf_get_cstring(req, &provider, NULL)) != 0 ||
  140. (r = sshbuf_get_cstring(req, &device, NULL)) != 0 ||
  141. (r = sshbuf_get_cstring(req, &application, NULL)) != 0 ||
  142. (r = sshbuf_get_cstring(req, &userid, NULL)) != 0 ||
  143. (r = sshbuf_get_u8(req, &flags)) != 0 ||
  144. (r = sshbuf_get_cstring(req, &pin, NULL)) != 0 ||
  145. (r = sshbuf_froms(req, &challenge)) != 0)
  146. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  147. if (sshbuf_len(req) != 0)
  148. fatal("%s: trailing data in request", __progname);
  149. if (type > INT_MAX)
  150. fatal("%s: bad type %u", __progname, type);
  151. if (sshbuf_len(challenge) == 0) {
  152. sshbuf_free(challenge);
  153. challenge = NULL;
  154. }
  155. null_empty(&device);
  156. null_empty(&userid);
  157. null_empty(&pin);
  158. if ((r = sshsk_enroll((int)type, provider, device, application, userid,
  159. flags, pin, challenge, &key, attest)) != 0) {
  160. resp = reply_error(r, "Enrollment failed: %s", ssh_err(r));
  161. goto out;
  162. }
  163. if ((resp = sshbuf_new()) == NULL)
  164. fatal("%s: sshbuf_new failed", __progname);
  165. if ((r = sshkey_private_serialize(key, kbuf)) != 0)
  166. fatal("%s: serialize private key: %s", __progname, ssh_err(r));
  167. if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_ENROLL)) != 0 ||
  168. (r = sshbuf_put_stringb(resp, kbuf)) != 0 ||
  169. (r = sshbuf_put_stringb(resp, attest)) != 0)
  170. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  171. out:
  172. sshkey_free(key);
  173. sshbuf_free(kbuf);
  174. sshbuf_free(attest);
  175. sshbuf_free(challenge);
  176. free(provider);
  177. free(application);
  178. if (pin != NULL)
  179. freezero(pin, strlen(pin));
  180. return resp;
  181. }
  182. static struct sshbuf *
  183. process_load_resident(struct sshbuf *req)
  184. {
  185. int r;
  186. char *provider, *pin, *device;
  187. struct sshbuf *kbuf, *resp;
  188. struct sshkey **keys = NULL;
  189. size_t nkeys = 0, i;
  190. if ((kbuf = sshbuf_new()) == NULL)
  191. fatal("%s: sshbuf_new failed", __progname);
  192. if ((r = sshbuf_get_cstring(req, &provider, NULL)) != 0 ||
  193. (r = sshbuf_get_cstring(req, &device, NULL)) != 0 ||
  194. (r = sshbuf_get_cstring(req, &pin, NULL)) != 0)
  195. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  196. if (sshbuf_len(req) != 0)
  197. fatal("%s: trailing data in request", __progname);
  198. null_empty(&device);
  199. null_empty(&pin);
  200. if ((r = sshsk_load_resident(provider, device, pin,
  201. &keys, &nkeys)) != 0) {
  202. resp = reply_error(r, " sshsk_load_resident failed: %s",
  203. ssh_err(r));
  204. goto out;
  205. }
  206. if ((resp = sshbuf_new()) == NULL)
  207. fatal("%s: sshbuf_new failed", __progname);
  208. if ((r = sshbuf_put_u32(resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0)
  209. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  210. for (i = 0; i < nkeys; i++) {
  211. debug("%s: key %zu %s %s", __func__, i,
  212. sshkey_type(keys[i]), keys[i]->sk_application);
  213. sshbuf_reset(kbuf);
  214. if ((r = sshkey_private_serialize(keys[i], kbuf)) != 0)
  215. fatal("%s: serialize private key: %s",
  216. __progname, ssh_err(r));
  217. if ((r = sshbuf_put_stringb(resp, kbuf)) != 0 ||
  218. (r = sshbuf_put_cstring(resp, "")) != 0) /* comment */
  219. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  220. }
  221. out:
  222. for (i = 0; i < nkeys; i++)
  223. sshkey_free(keys[i]);
  224. free(keys);
  225. sshbuf_free(kbuf);
  226. free(provider);
  227. if (pin != NULL)
  228. freezero(pin, strlen(pin));
  229. return resp;
  230. }
  231. int
  232. main(int argc, char **argv)
  233. {
  234. SyslogFacility log_facility = SYSLOG_FACILITY_AUTH;
  235. LogLevel log_level = SYSLOG_LEVEL_ERROR;
  236. struct sshbuf *req, *resp;
  237. int in, out, ch, r, vflag = 0;
  238. u_int rtype, ll = 0;
  239. uint8_t version, log_stderr = 0;
  240. sanitise_stdfd();
  241. log_init(__progname, log_level, log_facility, log_stderr);
  242. while ((ch = getopt(argc, argv, "v")) != -1) {
  243. switch (ch) {
  244. case 'v':
  245. vflag = 1;
  246. if (log_level == SYSLOG_LEVEL_ERROR)
  247. log_level = SYSLOG_LEVEL_DEBUG1;
  248. else if (log_level < SYSLOG_LEVEL_DEBUG3)
  249. log_level++;
  250. break;
  251. default:
  252. fprintf(stderr, "usage: %s [-v]\n", __progname);
  253. exit(1);
  254. }
  255. }
  256. log_init(__progname, log_level, log_facility, vflag);
  257. /*
  258. * Rearrange our file descriptors a little; we don't trust the
  259. * providers not to fiddle with stdin/out.
  260. */
  261. closefrom(STDERR_FILENO + 1);
  262. if ((in = dup(STDIN_FILENO)) == -1 || (out = dup(STDOUT_FILENO)) == -1)
  263. fatal("%s: dup: %s", __progname, strerror(errno));
  264. close(STDIN_FILENO);
  265. close(STDOUT_FILENO);
  266. sanitise_stdfd(); /* resets to /dev/null */
  267. if ((req = sshbuf_new()) == NULL)
  268. fatal("%s: sshbuf_new failed", __progname);
  269. if (ssh_msg_recv(in, req) < 0)
  270. fatal("ssh_msg_recv failed");
  271. close(in);
  272. debug("%s: received message len %zu", __progname, sshbuf_len(req));
  273. if ((r = sshbuf_get_u8(req, &version)) != 0)
  274. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  275. if (version != SSH_SK_HELPER_VERSION) {
  276. fatal("unsupported version: received %d, expected %d",
  277. version, SSH_SK_HELPER_VERSION);
  278. }
  279. if ((r = sshbuf_get_u32(req, &rtype)) != 0 ||
  280. (r = sshbuf_get_u8(req, &log_stderr)) != 0 ||
  281. (r = sshbuf_get_u32(req, &ll)) != 0)
  282. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  283. if (!vflag && log_level_name((LogLevel)ll) != NULL)
  284. log_init(__progname, (LogLevel)ll, log_facility, log_stderr);
  285. switch (rtype) {
  286. case SSH_SK_HELPER_SIGN:
  287. resp = process_sign(req);
  288. break;
  289. case SSH_SK_HELPER_ENROLL:
  290. resp = process_enroll(req);
  291. break;
  292. case SSH_SK_HELPER_LOAD_RESIDENT:
  293. resp = process_load_resident(req);
  294. break;
  295. default:
  296. fatal("%s: unsupported request type %u", __progname, rtype);
  297. }
  298. sshbuf_free(req);
  299. debug("%s: reply len %zu", __progname, sshbuf_len(resp));
  300. if (ssh_msg_send(out, SSH_SK_HELPER_VERSION, resp) == -1)
  301. fatal("ssh_msg_send failed");
  302. sshbuf_free(resp);
  303. close(out);
  304. return (0);
  305. }
  306. #else /* ENABLE_SK */
  307. #include <stdio.h>
  308. int
  309. main(int argc, char **argv)
  310. {
  311. fprintf(stderr, "ssh-sk-helper: disabled at compile time\n");
  312. return -1;
  313. }
  314. #endif /* ENABLE_SK */