ssh-keysign.c 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  1. /* $OpenBSD: ssh-keysign.c,v 1.64 2020/08/27 01:06:18 djm Exp $ */
  2. /*
  3. * Copyright (c) 2002 Markus Friedl. All rights reserved.
  4. *
  5. * Redistribution and use in source and binary forms, with or without
  6. * modification, are permitted provided that the following conditions
  7. * are met:
  8. * 1. Redistributions of source code must retain the above copyright
  9. * notice, this list of conditions and the following disclaimer.
  10. * 2. Redistributions in binary form must reproduce the above copyright
  11. * notice, this list of conditions and the following disclaimer in the
  12. * documentation and/or other materials provided with the distribution.
  13. *
  14. * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
  15. * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  16. * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
  17. * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
  18. * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
  19. * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  20. * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  21. * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  22. * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  23. * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  24. */
  25. #include "includes.h"
  26. #include <fcntl.h>
  27. #ifdef HAVE_PATHS_H
  28. #include <paths.h>
  29. #endif
  30. #include <pwd.h>
  31. #include <stdarg.h>
  32. #include <stdio.h>
  33. #include <stdlib.h>
  34. #include <string.h>
  35. #include <unistd.h>
  36. #include <errno.h>
  37. #ifdef WITH_OPENSSL
  38. #include <openssl/evp.h>
  39. #include <openssl/rand.h>
  40. #include <openssl/rsa.h>
  41. #include "openbsd-compat/openssl-compat.h"
  42. #endif
  43. #include "xmalloc.h"
  44. #include "log.h"
  45. #include "sshkey.h"
  46. #include "ssh.h"
  47. #include "ssh2.h"
  48. #include "misc.h"
  49. #include "sshbuf.h"
  50. #include "authfile.h"
  51. #include "msg.h"
  52. #include "canohost.h"
  53. #include "pathnames.h"
  54. #include "readconf.h"
  55. #include "uidswap.h"
  56. #include "ssherr.h"
  57. extern char *__progname;
  58. static int
  59. valid_request(struct passwd *pw, char *host, struct sshkey **ret,
  60. u_char *data, size_t datalen)
  61. {
  62. struct sshbuf *b;
  63. struct sshkey *key = NULL;
  64. u_char type, *pkblob;
  65. char *p;
  66. size_t blen, len;
  67. char *pkalg, *luser;
  68. int r, pktype, fail;
  69. if (ret != NULL)
  70. *ret = NULL;
  71. fail = 0;
  72. if ((b = sshbuf_from(data, datalen)) == NULL)
  73. fatal("%s: sshbuf_from failed", __func__);
  74. /* session id, currently limited to SHA1 (20 bytes) or SHA256 (32) */
  75. if ((r = sshbuf_get_string(b, NULL, &len)) != 0)
  76. fatal("%s: buffer error: %s", __func__, ssh_err(r));
  77. if (len != 20 && len != 32)
  78. fail++;
  79. if ((r = sshbuf_get_u8(b, &type)) != 0)
  80. fatal("%s: buffer error: %s", __func__, ssh_err(r));
  81. if (type != SSH2_MSG_USERAUTH_REQUEST)
  82. fail++;
  83. /* server user */
  84. if ((r = sshbuf_skip_string(b)) != 0)
  85. fatal("%s: buffer error: %s", __func__, ssh_err(r));
  86. /* service */
  87. if ((r = sshbuf_get_cstring(b, &p, NULL)) != 0)
  88. fatal("%s: buffer error: %s", __func__, ssh_err(r));
  89. if (strcmp("ssh-connection", p) != 0)
  90. fail++;
  91. free(p);
  92. /* method */
  93. if ((r = sshbuf_get_cstring(b, &p, NULL)) != 0)
  94. fatal("%s: buffer error: %s", __func__, ssh_err(r));
  95. if (strcmp("hostbased", p) != 0)
  96. fail++;
  97. free(p);
  98. /* pubkey */
  99. if ((r = sshbuf_get_cstring(b, &pkalg, NULL)) != 0 ||
  100. (r = sshbuf_get_string(b, &pkblob, &blen)) != 0)
  101. fatal("%s: buffer error: %s", __func__, ssh_err(r));
  102. pktype = sshkey_type_from_name(pkalg);
  103. if (pktype == KEY_UNSPEC)
  104. fail++;
  105. else if ((r = sshkey_from_blob(pkblob, blen, &key)) != 0) {
  106. error("%s: bad key blob: %s", __func__, ssh_err(r));
  107. fail++;
  108. } else if (key->type != pktype)
  109. fail++;
  110. free(pkalg);
  111. free(pkblob);
  112. /* client host name, handle trailing dot */
  113. if ((r = sshbuf_get_cstring(b, &p, &len)) != 0)
  114. fatal("%s: buffer error: %s", __func__, ssh_err(r));
  115. debug2("%s: check expect chost %s got %s", __func__, host, p);
  116. if (strlen(host) != len - 1)
  117. fail++;
  118. else if (p[len - 1] != '.')
  119. fail++;
  120. else if (strncasecmp(host, p, len - 1) != 0)
  121. fail++;
  122. free(p);
  123. /* local user */
  124. if ((r = sshbuf_get_cstring(b, &luser, NULL)) != 0)
  125. fatal("%s: buffer error: %s", __func__, ssh_err(r));
  126. if (strcmp(pw->pw_name, luser) != 0)
  127. fail++;
  128. free(luser);
  129. /* end of message */
  130. if (sshbuf_len(b) != 0)
  131. fail++;
  132. sshbuf_free(b);
  133. debug3("%s: fail %d", __func__, fail);
  134. if (fail)
  135. sshkey_free(key);
  136. else if (ret != NULL)
  137. *ret = key;
  138. return (fail ? -1 : 0);
  139. }
  140. int
  141. main(int argc, char **argv)
  142. {
  143. struct sshbuf *b;
  144. Options options;
  145. #define NUM_KEYTYPES 5
  146. struct sshkey *keys[NUM_KEYTYPES], *key = NULL;
  147. struct passwd *pw;
  148. int r, key_fd[NUM_KEYTYPES], i, found, version = 2, fd;
  149. u_char *signature, *data, rver;
  150. char *host, *fp;
  151. size_t slen, dlen;
  152. if (pledge("stdio rpath getpw dns id", NULL) != 0)
  153. fatal("%s: pledge: %s", __progname, strerror(errno));
  154. /* Ensure that stdin and stdout are connected */
  155. if ((fd = open(_PATH_DEVNULL, O_RDWR)) < 2)
  156. exit(1);
  157. /* Leave /dev/null fd iff it is attached to stderr */
  158. if (fd > 2)
  159. close(fd);
  160. i = 0;
  161. /* XXX This really needs to read sshd_config for the paths */
  162. key_fd[i++] = open(_PATH_HOST_DSA_KEY_FILE, O_RDONLY);
  163. key_fd[i++] = open(_PATH_HOST_ECDSA_KEY_FILE, O_RDONLY);
  164. key_fd[i++] = open(_PATH_HOST_ED25519_KEY_FILE, O_RDONLY);
  165. key_fd[i++] = open(_PATH_HOST_XMSS_KEY_FILE, O_RDONLY);
  166. key_fd[i++] = open(_PATH_HOST_RSA_KEY_FILE, O_RDONLY);
  167. if ((pw = getpwuid(getuid())) == NULL)
  168. fatal("getpwuid failed");
  169. pw = pwcopy(pw);
  170. permanently_set_uid(pw);
  171. seed_rng();
  172. #ifdef DEBUG_SSH_KEYSIGN
  173. log_init("ssh-keysign", SYSLOG_LEVEL_DEBUG3, SYSLOG_FACILITY_AUTH, 0);
  174. #endif
  175. /* verify that ssh-keysign is enabled by the admin */
  176. initialize_options(&options);
  177. (void)read_config_file(_PATH_HOST_CONFIG_FILE, pw, "", "",
  178. &options, 0, NULL);
  179. (void)fill_default_options(&options);
  180. if (options.enable_ssh_keysign != 1)
  181. fatal("ssh-keysign not enabled in %s",
  182. _PATH_HOST_CONFIG_FILE);
  183. for (i = found = 0; i < NUM_KEYTYPES; i++) {
  184. if (key_fd[i] != -1)
  185. found = 1;
  186. }
  187. if (found == 0)
  188. fatal("could not open any host key");
  189. found = 0;
  190. for (i = 0; i < NUM_KEYTYPES; i++) {
  191. keys[i] = NULL;
  192. if (key_fd[i] == -1)
  193. continue;
  194. r = sshkey_load_private_type_fd(key_fd[i], KEY_UNSPEC,
  195. NULL, &key, NULL);
  196. close(key_fd[i]);
  197. if (r != 0)
  198. debug("parse key %d: %s", i, ssh_err(r));
  199. else if (key != NULL) {
  200. keys[i] = key;
  201. found = 1;
  202. }
  203. }
  204. if (!found)
  205. fatal("no hostkey found");
  206. if (pledge("stdio dns", NULL) != 0)
  207. fatal("%s: pledge: %s", __progname, strerror(errno));
  208. if ((b = sshbuf_new()) == NULL)
  209. fatal("%s: sshbuf_new failed", __progname);
  210. if (ssh_msg_recv(STDIN_FILENO, b) < 0)
  211. fatal("ssh_msg_recv failed");
  212. if ((r = sshbuf_get_u8(b, &rver)) != 0)
  213. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  214. if (rver != version)
  215. fatal("bad version: received %d, expected %d", rver, version);
  216. if ((r = sshbuf_get_u32(b, (u_int *)&fd)) != 0)
  217. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  218. if (fd < 0 || fd == STDIN_FILENO || fd == STDOUT_FILENO)
  219. fatal("bad fd = %d", fd);
  220. if ((host = get_local_name(fd)) == NULL)
  221. fatal("cannot get local name for fd");
  222. if ((r = sshbuf_get_string(b, &data, &dlen)) != 0)
  223. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  224. if (valid_request(pw, host, &key, data, dlen) < 0)
  225. fatal("not a valid request");
  226. free(host);
  227. found = 0;
  228. for (i = 0; i < NUM_KEYTYPES; i++) {
  229. if (keys[i] != NULL &&
  230. sshkey_equal_public(key, keys[i])) {
  231. found = 1;
  232. break;
  233. }
  234. }
  235. if (!found) {
  236. if ((fp = sshkey_fingerprint(key, options.fingerprint_hash,
  237. SSH_FP_DEFAULT)) == NULL)
  238. fatal("%s: sshkey_fingerprint failed", __progname);
  239. fatal("no matching hostkey found for key %s %s",
  240. sshkey_type(key), fp ? fp : "");
  241. }
  242. if ((r = sshkey_sign(keys[i], &signature, &slen, data, dlen,
  243. NULL, NULL, NULL, 0)) != 0)
  244. fatal("sshkey_sign failed: %s", ssh_err(r));
  245. free(data);
  246. /* send reply */
  247. sshbuf_reset(b);
  248. if ((r = sshbuf_put_string(b, signature, slen)) != 0)
  249. fatal("%s: buffer error: %s", __progname, ssh_err(r));
  250. if (ssh_msg_send(STDOUT_FILENO, version, b) == -1)
  251. fatal("ssh_msg_send failed");
  252. return (0);
  253. }