ssh-sk-client.c 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450
  1. /* $OpenBSD: ssh-sk-client.c,v 1.7 2020/01/23 07:10:22 dtucker 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. #include "includes.h"
  18. #include <sys/types.h>
  19. #include <sys/socket.h>
  20. #include <sys/wait.h>
  21. #include <fcntl.h>
  22. #include <limits.h>
  23. #include <errno.h>
  24. #include <signal.h>
  25. #include <stdarg.h>
  26. #include <stdio.h>
  27. #include <stdlib.h>
  28. #include <string.h>
  29. #include <unistd.h>
  30. #include "log.h"
  31. #include "ssherr.h"
  32. #include "sshbuf.h"
  33. #include "sshkey.h"
  34. #include "msg.h"
  35. #include "digest.h"
  36. #include "pathnames.h"
  37. #include "ssh-sk.h"
  38. #include "misc.h"
  39. /* #define DEBUG_SK 1 */
  40. static int
  41. start_helper(int *fdp, pid_t *pidp, void (**osigchldp)(int))
  42. {
  43. void (*osigchld)(int);
  44. int oerrno, pair[2], r = SSH_ERR_INTERNAL_ERROR;
  45. pid_t pid;
  46. char *helper, *verbosity = NULL;
  47. *fdp = -1;
  48. *pidp = 0;
  49. *osigchldp = SIG_DFL;
  50. helper = getenv("SSH_SK_HELPER");
  51. if (helper == NULL || strlen(helper) == 0)
  52. helper = _PATH_SSH_SK_HELPER;
  53. if (access(helper, X_OK) != 0) {
  54. oerrno = errno;
  55. error("%s: helper \"%s\" unusable: %s", __func__, helper,
  56. strerror(errno));
  57. errno = oerrno;
  58. return SSH_ERR_SYSTEM_ERROR;
  59. }
  60. #ifdef DEBUG_SK
  61. verbosity = "-vvv";
  62. #endif
  63. /* Start helper */
  64. if (socketpair(AF_UNIX, SOCK_STREAM, 0, pair) == -1) {
  65. error("socketpair: %s", strerror(errno));
  66. return SSH_ERR_SYSTEM_ERROR;
  67. }
  68. osigchld = ssh_signal(SIGCHLD, SIG_DFL);
  69. if ((pid = fork()) == -1) {
  70. oerrno = errno;
  71. error("fork: %s", strerror(errno));
  72. close(pair[0]);
  73. close(pair[1]);
  74. ssh_signal(SIGCHLD, osigchld);
  75. errno = oerrno;
  76. return SSH_ERR_SYSTEM_ERROR;
  77. }
  78. if (pid == 0) {
  79. if ((dup2(pair[1], STDIN_FILENO) == -1) ||
  80. (dup2(pair[1], STDOUT_FILENO) == -1)) {
  81. error("%s: dup2: %s", __func__, ssh_err(r));
  82. _exit(1);
  83. }
  84. close(pair[0]);
  85. close(pair[1]);
  86. closefrom(STDERR_FILENO + 1);
  87. debug("%s: starting %s %s", __func__, helper,
  88. verbosity == NULL ? "" : verbosity);
  89. execlp(helper, helper, verbosity, (char *)NULL);
  90. error("%s: execlp: %s", __func__, strerror(errno));
  91. _exit(1);
  92. }
  93. close(pair[1]);
  94. /* success */
  95. debug3("%s: started pid=%ld", __func__, (long)pid);
  96. *fdp = pair[0];
  97. *pidp = pid;
  98. *osigchldp = osigchld;
  99. return 0;
  100. }
  101. static int
  102. reap_helper(pid_t pid)
  103. {
  104. int status, oerrno;
  105. debug3("%s: pid=%ld", __func__, (long)pid);
  106. errno = 0;
  107. while (waitpid(pid, &status, 0) == -1) {
  108. if (errno == EINTR) {
  109. errno = 0;
  110. continue;
  111. }
  112. oerrno = errno;
  113. error("%s: waitpid: %s", __func__, strerror(errno));
  114. errno = oerrno;
  115. return SSH_ERR_SYSTEM_ERROR;
  116. }
  117. if (!WIFEXITED(status)) {
  118. error("%s: helper exited abnormally", __func__);
  119. return SSH_ERR_AGENT_FAILURE;
  120. } else if (WEXITSTATUS(status) != 0) {
  121. error("%s: helper exited with non-zero exit status", __func__);
  122. return SSH_ERR_AGENT_FAILURE;
  123. }
  124. return 0;
  125. }
  126. static int
  127. client_converse(struct sshbuf *msg, struct sshbuf **respp, u_int type)
  128. {
  129. int oerrno, fd, r2, ll, r = SSH_ERR_INTERNAL_ERROR;
  130. u_int rtype, rerr;
  131. pid_t pid;
  132. u_char version;
  133. void (*osigchld)(int);
  134. struct sshbuf *req = NULL, *resp = NULL;
  135. *respp = NULL;
  136. if ((r = start_helper(&fd, &pid, &osigchld)) != 0)
  137. return r;
  138. if ((req = sshbuf_new()) == NULL || (resp = sshbuf_new()) == NULL) {
  139. r = SSH_ERR_ALLOC_FAIL;
  140. goto out;
  141. }
  142. /* Request preamble: type, log_on_stderr, log_level */
  143. ll = log_level_get();
  144. if ((r = sshbuf_put_u32(req, type)) != 0 ||
  145. (r = sshbuf_put_u8(req, log_is_on_stderr() != 0)) != 0 ||
  146. (r = sshbuf_put_u32(req, ll < 0 ? 0 : ll)) != 0 ||
  147. (r = sshbuf_putb(req, msg)) != 0) {
  148. error("%s: build: %s", __func__, ssh_err(r));
  149. goto out;
  150. }
  151. if ((r = ssh_msg_send(fd, SSH_SK_HELPER_VERSION, req)) != 0) {
  152. error("%s: send: %s", __func__, ssh_err(r));
  153. goto out;
  154. }
  155. if ((r = ssh_msg_recv(fd, resp)) != 0) {
  156. error("%s: receive: %s", __func__, ssh_err(r));
  157. goto out;
  158. }
  159. if ((r = sshbuf_get_u8(resp, &version)) != 0) {
  160. error("%s: parse version: %s", __func__, ssh_err(r));
  161. goto out;
  162. }
  163. if (version != SSH_SK_HELPER_VERSION) {
  164. error("%s: unsupported version: got %u, expected %u",
  165. __func__, version, SSH_SK_HELPER_VERSION);
  166. r = SSH_ERR_INVALID_FORMAT;
  167. goto out;
  168. }
  169. if ((r = sshbuf_get_u32(resp, &rtype)) != 0) {
  170. error("%s: parse message type: %s", __func__, ssh_err(r));
  171. goto out;
  172. }
  173. if (rtype == SSH_SK_HELPER_ERROR) {
  174. if ((r = sshbuf_get_u32(resp, &rerr)) != 0) {
  175. error("%s: parse error: %s", __func__, ssh_err(r));
  176. goto out;
  177. }
  178. debug("%s: helper returned error -%u", __func__, rerr);
  179. /* OpenSSH error values are negative; encoded as -err on wire */
  180. if (rerr == 0 || rerr >= INT_MAX)
  181. r = SSH_ERR_INTERNAL_ERROR;
  182. else
  183. r = -(int)rerr;
  184. goto out;
  185. } else if (rtype != type) {
  186. error("%s: helper returned incorrect message type %u, "
  187. "expecting %u", __func__, rtype, type);
  188. r = SSH_ERR_INTERNAL_ERROR;
  189. goto out;
  190. }
  191. /* success */
  192. r = 0;
  193. out:
  194. oerrno = errno;
  195. close(fd);
  196. if ((r2 = reap_helper(pid)) != 0) {
  197. if (r == 0) {
  198. r = r2;
  199. oerrno = errno;
  200. }
  201. }
  202. if (r == 0) {
  203. *respp = resp;
  204. resp = NULL;
  205. }
  206. sshbuf_free(req);
  207. sshbuf_free(resp);
  208. ssh_signal(SIGCHLD, osigchld);
  209. errno = oerrno;
  210. return r;
  211. }
  212. int
  213. sshsk_sign(const char *provider, struct sshkey *key,
  214. u_char **sigp, size_t *lenp, const u_char *data, size_t datalen,
  215. u_int compat, const char *pin)
  216. {
  217. int oerrno, r = SSH_ERR_INTERNAL_ERROR;
  218. char *fp = NULL;
  219. struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
  220. *sigp = NULL;
  221. *lenp = 0;
  222. #ifndef ENABLE_SK
  223. return SSH_ERR_KEY_TYPE_UNKNOWN;
  224. #endif
  225. if ((kbuf = sshbuf_new()) == NULL ||
  226. (req = sshbuf_new()) == NULL) {
  227. r = SSH_ERR_ALLOC_FAIL;
  228. goto out;
  229. }
  230. if ((r = sshkey_private_serialize(key, kbuf)) != 0) {
  231. error("%s: serialize private key: %s", __func__, ssh_err(r));
  232. goto out;
  233. }
  234. if ((r = sshbuf_put_stringb(req, kbuf)) != 0 ||
  235. (r = sshbuf_put_cstring(req, provider)) != 0 ||
  236. (r = sshbuf_put_string(req, data, datalen)) != 0 ||
  237. (r = sshbuf_put_cstring(req, NULL)) != 0 || /* alg */
  238. (r = sshbuf_put_u32(req, compat)) != 0 ||
  239. (r = sshbuf_put_cstring(req, pin)) != 0) {
  240. error("%s: compose: %s", __func__, ssh_err(r));
  241. goto out;
  242. }
  243. if ((fp = sshkey_fingerprint(key, SSH_FP_HASH_DEFAULT,
  244. SSH_FP_DEFAULT)) == NULL) {
  245. error("%s: sshkey_fingerprint failed", __func__);
  246. r = SSH_ERR_ALLOC_FAIL;
  247. goto out;
  248. }
  249. if ((r = client_converse(req, &resp, SSH_SK_HELPER_SIGN)) != 0)
  250. goto out;
  251. if ((r = sshbuf_get_string(resp, sigp, lenp)) != 0) {
  252. error("%s: parse signature: %s", __func__, ssh_err(r));
  253. r = SSH_ERR_INVALID_FORMAT;
  254. goto out;
  255. }
  256. if (sshbuf_len(resp) != 0) {
  257. error("%s: trailing data in response", __func__);
  258. r = SSH_ERR_INVALID_FORMAT;
  259. goto out;
  260. }
  261. /* success */
  262. r = 0;
  263. out:
  264. oerrno = errno;
  265. if (r != 0) {
  266. freezero(*sigp, *lenp);
  267. *sigp = NULL;
  268. *lenp = 0;
  269. }
  270. sshbuf_free(kbuf);
  271. sshbuf_free(req);
  272. sshbuf_free(resp);
  273. errno = oerrno;
  274. return r;
  275. }
  276. int
  277. sshsk_enroll(int type, const char *provider_path, const char *device,
  278. const char *application, const char *userid, uint8_t flags,
  279. const char *pin, struct sshbuf *challenge_buf,
  280. struct sshkey **keyp, struct sshbuf *attest)
  281. {
  282. int oerrno, r = SSH_ERR_INTERNAL_ERROR;
  283. struct sshbuf *kbuf = NULL, *abuf = NULL, *req = NULL, *resp = NULL;
  284. struct sshkey *key = NULL;
  285. *keyp = NULL;
  286. if (attest != NULL)
  287. sshbuf_reset(attest);
  288. #ifndef ENABLE_SK
  289. return SSH_ERR_KEY_TYPE_UNKNOWN;
  290. #endif
  291. if (type < 0)
  292. return SSH_ERR_INVALID_ARGUMENT;
  293. if ((abuf = sshbuf_new()) == NULL ||
  294. (kbuf = sshbuf_new()) == NULL ||
  295. (req = sshbuf_new()) == NULL) {
  296. r = SSH_ERR_ALLOC_FAIL;
  297. goto out;
  298. }
  299. if ((r = sshbuf_put_u32(req, (u_int)type)) != 0 ||
  300. (r = sshbuf_put_cstring(req, provider_path)) != 0 ||
  301. (r = sshbuf_put_cstring(req, device)) != 0 ||
  302. (r = sshbuf_put_cstring(req, application)) != 0 ||
  303. (r = sshbuf_put_cstring(req, userid)) != 0 ||
  304. (r = sshbuf_put_u8(req, flags)) != 0 ||
  305. (r = sshbuf_put_cstring(req, pin)) != 0 ||
  306. (r = sshbuf_put_stringb(req, challenge_buf)) != 0) {
  307. error("%s: compose: %s", __func__, ssh_err(r));
  308. goto out;
  309. }
  310. if ((r = client_converse(req, &resp, SSH_SK_HELPER_ENROLL)) != 0)
  311. goto out;
  312. if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
  313. (r = sshbuf_get_stringb(resp, abuf)) != 0) {
  314. error("%s: parse signature: %s", __func__, ssh_err(r));
  315. r = SSH_ERR_INVALID_FORMAT;
  316. goto out;
  317. }
  318. if (sshbuf_len(resp) != 0) {
  319. error("%s: trailing data in response", __func__);
  320. r = SSH_ERR_INVALID_FORMAT;
  321. goto out;
  322. }
  323. if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
  324. error("Unable to parse private key: %s", ssh_err(r));
  325. goto out;
  326. }
  327. if (attest != NULL && (r = sshbuf_putb(attest, abuf)) != 0) {
  328. error("%s: buffer error: %s", __func__, ssh_err(r));
  329. goto out;
  330. }
  331. /* success */
  332. r = 0;
  333. *keyp = key;
  334. key = NULL;
  335. out:
  336. oerrno = errno;
  337. sshkey_free(key);
  338. sshbuf_free(kbuf);
  339. sshbuf_free(abuf);
  340. sshbuf_free(req);
  341. sshbuf_free(resp);
  342. errno = oerrno;
  343. return r;
  344. }
  345. int
  346. sshsk_load_resident(const char *provider_path, const char *device,
  347. const char *pin, struct sshkey ***keysp, size_t *nkeysp)
  348. {
  349. int oerrno, r = SSH_ERR_INTERNAL_ERROR;
  350. struct sshbuf *kbuf = NULL, *req = NULL, *resp = NULL;
  351. struct sshkey *key = NULL, **keys = NULL, **tmp;
  352. size_t i, nkeys = 0;
  353. *keysp = NULL;
  354. *nkeysp = 0;
  355. if ((resp = sshbuf_new()) == NULL ||
  356. (kbuf = sshbuf_new()) == NULL ||
  357. (req = sshbuf_new()) == NULL) {
  358. r = SSH_ERR_ALLOC_FAIL;
  359. goto out;
  360. }
  361. if ((r = sshbuf_put_cstring(req, provider_path)) != 0 ||
  362. (r = sshbuf_put_cstring(req, device)) != 0 ||
  363. (r = sshbuf_put_cstring(req, pin)) != 0) {
  364. error("%s: compose: %s", __func__, ssh_err(r));
  365. goto out;
  366. }
  367. if ((r = client_converse(req, &resp, SSH_SK_HELPER_LOAD_RESIDENT)) != 0)
  368. goto out;
  369. while (sshbuf_len(resp) != 0) {
  370. /* key, comment */
  371. if ((r = sshbuf_get_stringb(resp, kbuf)) != 0 ||
  372. (r = sshbuf_get_cstring(resp, NULL, NULL)) != 0) {
  373. error("%s: parse signature: %s", __func__, ssh_err(r));
  374. r = SSH_ERR_INVALID_FORMAT;
  375. goto out;
  376. }
  377. if ((r = sshkey_private_deserialize(kbuf, &key)) != 0) {
  378. error("Unable to parse private key: %s", ssh_err(r));
  379. goto out;
  380. }
  381. if ((tmp = recallocarray(keys, nkeys, nkeys + 1,
  382. sizeof(*keys))) == NULL) {
  383. error("%s: recallocarray keys failed", __func__);
  384. goto out;
  385. }
  386. debug("%s: keys[%zu]: %s %s", __func__,
  387. nkeys, sshkey_type(key), key->sk_application);
  388. keys = tmp;
  389. keys[nkeys++] = key;
  390. key = NULL;
  391. }
  392. /* success */
  393. r = 0;
  394. *keysp = keys;
  395. *nkeysp = nkeys;
  396. keys = NULL;
  397. nkeys = 0;
  398. out:
  399. oerrno = errno;
  400. for (i = 0; i < nkeys; i++)
  401. sshkey_free(keys[i]);
  402. free(keys);
  403. sshkey_free(key);
  404. sshbuf_free(kbuf);
  405. sshbuf_free(req);
  406. sshbuf_free(resp);
  407. errno = oerrno;
  408. return r;
  409. }