openssh-certs.c 45 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227
  1. /*
  2. * Public key type for OpenSSH certificates.
  3. */
  4. #include "ssh.h"
  5. #include "putty.h"
  6. enum {
  7. SSH_CERT_TYPE_USER = 1,
  8. SSH_CERT_TYPE_HOST = 2,
  9. };
  10. typedef struct opensshcert_key {
  11. strbuf *nonce;
  12. uint64_t serial;
  13. uint32_t type;
  14. strbuf *key_id;
  15. strbuf *valid_principals;
  16. uint64_t valid_after, valid_before;
  17. strbuf *critical_options;
  18. strbuf *extensions;
  19. strbuf *reserved;
  20. strbuf *signature_key;
  21. strbuf *signature;
  22. ssh_key *basekey;
  23. ssh_key sshk;
  24. } opensshcert_key;
  25. typedef struct blob_fmt {
  26. const unsigned *fmt;
  27. size_t len;
  28. } blob_fmt;
  29. typedef struct opensshcert_extra {
  30. /*
  31. * OpenSSH certificate formats aren't completely consistent about
  32. * the relationship between the public+private blob uploaded to
  33. * the agent for the certified key type, and the one for the base
  34. * key type. Here we specify the mapping.
  35. *
  36. * Each of these foo_fmt strings indicates the layout of a
  37. * particular version of the key, in the form of an array of
  38. * integers together with a length, with each integer describing
  39. * one of the components of the key. The integers are defined by
  40. * enums, so that they're tightly packed; the general idea is that
  41. * if you're converting from one form to another, then you use the
  42. * format list for the source format to read out a succession of
  43. * SSH strings from the source data and put them in an array
  44. * indexed by the integer ids, and then use the list for the
  45. * destination format to write the strings out to the destination
  46. * in the right (maybe different) order.
  47. *
  48. * pub_fmt describes the format of the public-key blob for the
  49. * base key type, not counting the initial string giving the key
  50. * type identifier itself. As far as I know, this always matches
  51. * the format of the public-key data appearing in the middle of
  52. * the certificate.
  53. *
  54. * base_ossh_fmt describes the format of the full OpenSSH blob
  55. * appearing in the ssh-agent protocol for the base key,
  56. * containing the public and private key data.
  57. *
  58. * cert_ossh_fmt describes the format of the OpenSSH blob for the
  59. * certificate key format, beginning just after the certificate
  60. * string itself.
  61. */
  62. blob_fmt pub_fmt, base_ossh_fmt, cert_ossh_fmt;
  63. /*
  64. * The RSA-SHA2 algorithm names have their SSH id set to names
  65. * like "rsa-sha2-512-cert-...", which is what will be received in
  66. * the KEXINIT algorithm list if a host key in one of those
  67. * algorithms is presented. But the _key_ type id that will appear
  68. * in the public key blob is "ssh-rsa-cert-...". So we need a
  69. * separate field to indicate the key type id we expect to see in
  70. * certified public keys, and also the one we want to put back
  71. * into the artificial public blob we make to pass to the
  72. * constructor for the underlying key.
  73. *
  74. * (In rsa.c this is managed much more simply, because everything
  75. * sharing the same vtable wants the same key type id.)
  76. */
  77. const char *cert_key_ssh_id, *base_key_ssh_id;
  78. } opensshcert_extra;
  79. /*
  80. * The actual integer arrays defining the per-key blob formats.
  81. */
  82. /* DSA is the most orthodox: only the obviously necessary public key
  83. * info appears at all, it's in the same order everywhere, and none of
  84. * it is repeated unnecessarily */
  85. enum { DSA_p, DSA_q, DSA_g, DSA_y, DSA_x };
  86. static const unsigned dsa_pub_fmt[] = { DSA_p, DSA_q, DSA_g, DSA_y };
  87. static const unsigned dsa_base_ossh_fmt[] = {
  88. DSA_p, DSA_q, DSA_g, DSA_y, DSA_x };
  89. static const unsigned dsa_cert_ossh_fmt[] = { DSA_x };
  90. /* ECDSA is almost as nice, except that it pointlessly mentions the
  91. * curve name in the public data, which shouldn't be necessary given
  92. * that the SSH key id has already implied it. But at least that's
  93. * consistent everywhere. */
  94. enum { ECDSA_curve, ECDSA_point, ECDSA_exp };
  95. static const unsigned ecdsa_pub_fmt[] = { ECDSA_curve, ECDSA_point };
  96. static const unsigned ecdsa_base_ossh_fmt[] = {
  97. ECDSA_curve, ECDSA_point, ECDSA_exp };
  98. static const unsigned ecdsa_cert_ossh_fmt[] = { ECDSA_exp };
  99. /* Ed25519 has the oddity that the private data following the
  100. * certificate in the OpenSSH blob is preceded by an extra copy of the
  101. * public data, for no obviously necessary reason since that doesn't
  102. * happen in any of the rest of these formats */
  103. enum { EDDSA_point, EDDSA_exp };
  104. static const unsigned eddsa_pub_fmt[] = { EDDSA_point };
  105. static const unsigned eddsa_base_ossh_fmt[] = { EDDSA_point, EDDSA_exp };
  106. static const unsigned eddsa_cert_ossh_fmt[] = { EDDSA_point, EDDSA_exp };
  107. /* And RSA has the quirk that the modulus and exponent are reversed in
  108. * the base key type's OpenSSH blob! */
  109. enum { RSA_e, RSA_n, RSA_d, RSA_p, RSA_q, RSA_iqmp };
  110. static const unsigned rsa_pub_fmt[] = { RSA_e, RSA_n };
  111. static const unsigned rsa_base_ossh_fmt[] = {
  112. RSA_n, RSA_e, RSA_d, RSA_p, RSA_q, RSA_iqmp };
  113. static const unsigned rsa_cert_ossh_fmt[] = { RSA_d, RSA_p, RSA_q, RSA_iqmp };
  114. /*
  115. * Routines to transform one kind of blob into another based on those
  116. * foo_fmt integer arrays.
  117. */
  118. typedef struct BlobTransformer {
  119. ptrlen *parts;
  120. size_t nparts;
  121. } BlobTransformer;
  122. #define BLOBTRANS_DECLARE(bt) BlobTransformer bt[1] = { { NULL, 0 } }
  123. static inline void blobtrans_clear(BlobTransformer *bt)
  124. {
  125. sfree(bt->parts);
  126. bt->parts = NULL;
  127. bt->nparts = 0;
  128. }
  129. static inline bool blobtrans_read(BlobTransformer *bt, BinarySource *src,
  130. blob_fmt blob)
  131. {
  132. size_t nparts = bt->nparts;
  133. { // WINSCP
  134. size_t i;
  135. for (i = 0; i < blob.len; i++)
  136. if (nparts < blob.fmt[i]+1)
  137. nparts = blob.fmt[i]+1;
  138. if (nparts > bt->nparts) {
  139. bt->parts = sresize(bt->parts, nparts, ptrlen);
  140. while (bt->nparts < nparts)
  141. bt->parts[bt->nparts++] = make_ptrlen(NULL, 0);
  142. }
  143. for (i = 0; i < blob.len; i++) {
  144. size_t j = blob.fmt[i];
  145. ptrlen part = get_string(src);
  146. if (bt->parts[j].ptr) {
  147. /*
  148. * If the same string appears in both the public blob and
  149. * the private data, check they match. (This happens in
  150. * Ed25519: an extra copy of the public point string
  151. * appears in the certified OpenSSH data after the
  152. * certificate and before the private key.)
  153. */
  154. if (!ptrlen_eq_ptrlen(bt->parts[j], part))
  155. return false;
  156. }
  157. bt->parts[j] = part;
  158. }
  159. return true;
  160. } // WINSCP
  161. }
  162. static inline void blobtrans_write(BlobTransformer *bt, BinarySink *bs,
  163. blob_fmt blob)
  164. {
  165. size_t i; // WINSCP
  166. for (i = 0; i < blob.len; i++) {
  167. pinitassert(i < bt->nparts);
  168. ptrlen part = bt->parts[blob.fmt[i]];
  169. assert(part.ptr);
  170. put_stringpl(bs, part);
  171. }
  172. }
  173. /*
  174. * Forward declarations.
  175. */
  176. static ssh_key *opensshcert_new_pub(const ssh_keyalg *self, ptrlen pub);
  177. static ssh_key *opensshcert_new_priv(
  178. const ssh_keyalg *self, ptrlen pub, ptrlen priv);
  179. static ssh_key *opensshcert_new_priv_openssh(
  180. const ssh_keyalg *self, BinarySource *src);
  181. static void opensshcert_freekey(ssh_key *key);
  182. static char *opensshcert_invalid(ssh_key *key, unsigned flags);
  183. static void opensshcert_sign(ssh_key *key, ptrlen data, unsigned flags,
  184. BinarySink *bs);
  185. static bool opensshcert_verify(ssh_key *key, ptrlen sig, ptrlen data);
  186. static void opensshcert_public_blob(ssh_key *key, BinarySink *bs);
  187. static void opensshcert_private_blob(ssh_key *key, BinarySink *bs);
  188. static void opensshcert_openssh_blob(ssh_key *key, BinarySink *bs);
  189. static void opensshcert_ca_public_blob(ssh_key *key, BinarySink *bs);
  190. static void opensshcert_cert_id_string(ssh_key *key, BinarySink *bs);
  191. static SeatDialogText *opensshcert_cert_info(ssh_key *key);
  192. static bool opensshcert_has_private(ssh_key *key);
  193. static char *opensshcert_cache_str(ssh_key *key);
  194. static key_components *opensshcert_components(ssh_key *key);
  195. static ssh_key *opensshcert_base_key(ssh_key *key);
  196. static bool opensshcert_check_cert(
  197. ssh_key *key, bool host, ptrlen principal, uint64_t time,
  198. const ca_options *opts, BinarySink *error);
  199. static int opensshcert_pubkey_bits(const ssh_keyalg *self, ptrlen blob);
  200. static unsigned opensshcert_supported_flags(const ssh_keyalg *self);
  201. static const char *opensshcert_alternate_ssh_id(const ssh_keyalg *self,
  202. unsigned flags);
  203. static char *opensshcert_alg_desc(const ssh_keyalg *self);
  204. static bool opensshcert_variable_size(const ssh_keyalg *self);
  205. static const ssh_keyalg *opensshcert_related_alg(const ssh_keyalg *self,
  206. const ssh_keyalg *base);
  207. /*
  208. * Top-level vtables for the certified key formats, defined via a list
  209. * macro so I can also make an array of them all.
  210. */
  211. #define KEYALG_LIST(X) \
  212. X(ssh_dsa, "ssh-dss", "ssh-dss", dsa) \
  213. X(ssh_rsa, "ssh-rsa", "ssh-rsa", rsa) \
  214. X(ssh_rsa_sha256, "rsa-sha2-256", "ssh-rsa", rsa) \
  215. X(ssh_rsa_sha512, "rsa-sha2-512", "ssh-rsa", rsa) \
  216. X(ssh_ecdsa_ed25519, "ssh-ed25519", "ssh-ed25519", eddsa) \
  217. X(ssh_ecdsa_nistp256, "ecdsa-sha2-nistp256","ecdsa-sha2-nistp256", ecdsa) \
  218. X(ssh_ecdsa_nistp384, "ecdsa-sha2-nistp384","ecdsa-sha2-nistp384", ecdsa) \
  219. X(ssh_ecdsa_nistp521, "ecdsa-sha2-nistp521","ecdsa-sha2-nistp521", ecdsa) \
  220. /* end of list */
  221. #define KEYALG_DEF(name, ssh_alg_id_prefix, ssh_key_id_prefix, fmt_prefix) \
  222. static const struct opensshcert_extra opensshcert_##name##_extra = { \
  223. /*.pub_fmt =*/ { /*.fmt =*/ fmt_prefix ## _pub_fmt, \
  224. /*.len =*/ lenof(fmt_prefix ## _pub_fmt) }, \
  225. /*.base_ossh_fmt =*/ { /*.fmt =*/ fmt_prefix ## _base_ossh_fmt, \
  226. /*.len =*/ lenof(fmt_prefix ## _base_ossh_fmt) }, \
  227. /*.cert_ossh_fmt =*/ { /*.fmt =*/ fmt_prefix ## _cert_ossh_fmt, \
  228. /*.len =*/ lenof(fmt_prefix ## _cert_ossh_fmt) }, \
  229. /*.cert_key_ssh_id =*/ ssh_key_id_prefix "[email protected]", \
  230. /*.base_key_ssh_id =*/ ssh_key_id_prefix, \
  231. }; \
  232. \
  233. const ssh_keyalg opensshcert_##name = { \
  234. /*.new_pub =*/ opensshcert_new_pub, \
  235. /*.new_priv =*/ opensshcert_new_priv, \
  236. /*.new_priv_openssh =*/ opensshcert_new_priv_openssh, \
  237. /*.freekey =*/ opensshcert_freekey, \
  238. /*.invalid =*/ opensshcert_invalid, \
  239. /*.sign =*/ opensshcert_sign, \
  240. /*.verify =*/ opensshcert_verify, \
  241. /*.public_blob =*/ opensshcert_public_blob, \
  242. /*.private_blob =*/ opensshcert_private_blob, \
  243. /*.openssh_blob =*/ opensshcert_openssh_blob, \
  244. /*.has_private =*/ opensshcert_has_private, \
  245. /*.cache_str =*/ opensshcert_cache_str, \
  246. /*.components =*/ opensshcert_components, \
  247. /*.base_key =*/ opensshcert_base_key, \
  248. /*.ca_public_blob =*/ opensshcert_ca_public_blob, \
  249. /*.check_cert =*/ opensshcert_check_cert, \
  250. /*.cert_id_string =*/ opensshcert_cert_id_string, \
  251. /*.cert_info =*/ opensshcert_cert_info, \
  252. /*.pubkey_bits =*/ opensshcert_pubkey_bits, \
  253. /*.supported_flags =*/ opensshcert_supported_flags, \
  254. /*.alternate_ssh_id =*/ opensshcert_alternate_ssh_id, \
  255. /*.alg_desc =*/ opensshcert_alg_desc, \
  256. /*.variable_size =*/ opensshcert_variable_size, \
  257. /*.related_alg =*/ opensshcert_related_alg, \
  258. /*.ssh_id =*/ ssh_alg_id_prefix "[email protected]", \
  259. /*.cache_id =*/ "opensshcert-" ssh_key_id_prefix, \
  260. /*.extra =*/ &opensshcert_##name##_extra, \
  261. /*.is_certificate =*/ true, \
  262. /*.base_alg =*/ &name, \
  263. };
  264. KEYALG_LIST(KEYALG_DEF)
  265. #undef KEYALG_DEF
  266. #define KEYALG_LIST_ENTRY(name, algid, keyid, fmt) &opensshcert_##name,
  267. static const ssh_keyalg *const opensshcert_all_keyalgs[] = {
  268. KEYALG_LIST(KEYALG_LIST_ENTRY)
  269. };
  270. #undef KEYALG_LIST_ENTRY
  271. static strbuf *get_base_public_blob(BinarySource *src,
  272. const opensshcert_extra *extra)
  273. {
  274. strbuf *basepub = strbuf_new();
  275. put_stringz(basepub, extra->base_key_ssh_id);
  276. /* Make the base public key blob out of the public key
  277. * material in the certificate. This invocation of the
  278. * blobtrans system doesn't do any format translation, but it
  279. * does ensure that the right amount of data is copied so that
  280. * src ends up in the right position to read the remaining
  281. * certificate fields. */
  282. { // WINSCP
  283. BLOBTRANS_DECLARE(bt);
  284. blobtrans_read(bt, src, extra->pub_fmt);
  285. blobtrans_write(bt, BinarySink_UPCAST(basepub), extra->pub_fmt);
  286. blobtrans_clear(bt);
  287. return basepub;
  288. } // WINSCP
  289. }
  290. static opensshcert_key *opensshcert_new_shared(
  291. const ssh_keyalg *self, ptrlen blob, strbuf **basepub_out)
  292. {
  293. const opensshcert_extra *extra = self->extra;
  294. BinarySource src[1];
  295. BinarySource_BARE_INIT_PL(src, blob);
  296. /* Check the initial key-type string */
  297. if (!ptrlen_eq_string(get_string(src), extra->cert_key_ssh_id))
  298. return NULL;
  299. { // WINSCP
  300. opensshcert_key *ck = snew(opensshcert_key);
  301. memset(ck, 0, sizeof(*ck));
  302. ck->sshk.vt = self;
  303. ck->nonce = strbuf_dup(get_string(src));
  304. { // WINSCP
  305. strbuf *basepub = get_base_public_blob(src, extra);
  306. ck->serial = get_uint64(src);
  307. ck->type = get_uint32(src);
  308. ck->key_id = strbuf_dup(get_string(src));
  309. ck->valid_principals = strbuf_dup(get_string(src));
  310. ck->valid_after = get_uint64(src);
  311. ck->valid_before = get_uint64(src);
  312. ck->critical_options = strbuf_dup(get_string(src));
  313. ck->extensions = strbuf_dup(get_string(src));
  314. ck->reserved = strbuf_dup(get_string(src));
  315. ck->signature_key = strbuf_dup(get_string(src));
  316. ck->signature = strbuf_dup(get_string(src));
  317. if (get_err(src)) {
  318. ssh_key_free(&ck->sshk);
  319. strbuf_free(basepub);
  320. return NULL;
  321. }
  322. *basepub_out = basepub;
  323. return ck;
  324. } // WINSCP
  325. } // WINSCP
  326. }
  327. static ssh_key *opensshcert_new_pub(const ssh_keyalg *self, ptrlen pub)
  328. {
  329. strbuf *basepub;
  330. opensshcert_key *ck = opensshcert_new_shared(self, pub, &basepub);
  331. if (!ck)
  332. return NULL;
  333. ck->basekey = ssh_key_new_pub(self->base_alg, ptrlen_from_strbuf(basepub));
  334. strbuf_free(basepub);
  335. if (!ck->basekey) {
  336. ssh_key_free(&ck->sshk);
  337. return NULL;
  338. }
  339. return &ck->sshk;
  340. }
  341. static ssh_key *opensshcert_new_priv(
  342. const ssh_keyalg *self, ptrlen pub, ptrlen priv)
  343. {
  344. strbuf *basepub;
  345. opensshcert_key *ck = opensshcert_new_shared(self, pub, &basepub);
  346. if (!ck)
  347. return NULL;
  348. ck->basekey = ssh_key_new_priv(self->base_alg,
  349. ptrlen_from_strbuf(basepub), priv);
  350. strbuf_free(basepub);
  351. if (!ck->basekey) {
  352. ssh_key_free(&ck->sshk);
  353. return NULL;
  354. }
  355. return &ck->sshk;
  356. }
  357. static ssh_key *opensshcert_new_priv_openssh(
  358. const ssh_keyalg *self, BinarySource *src)
  359. {
  360. const opensshcert_extra *extra = self->extra;
  361. ptrlen cert = get_string(src);
  362. strbuf *basepub;
  363. opensshcert_key *ck = opensshcert_new_shared(self, cert, &basepub);
  364. if (!ck)
  365. return NULL;
  366. { // WINSCP
  367. strbuf *baseossh = strbuf_new();
  368. /* Make the base OpenSSH key blob out of the public key blob
  369. * returned from opensshcert_new_shared, and the trailing
  370. * private data following the certificate */
  371. BLOBTRANS_DECLARE(bt);
  372. BinarySource pubsrc[1];
  373. BinarySource_BARE_INIT_PL(pubsrc, ptrlen_from_strbuf(basepub));
  374. get_string(pubsrc); /* skip key type id */
  375. /* blobtrans_read might fail in this case, because we're reading
  376. * from two sources and they might fail to match */
  377. { // WINSCP
  378. bool success = blobtrans_read(bt, pubsrc, extra->pub_fmt) &&
  379. blobtrans_read(bt, src, extra->cert_ossh_fmt);
  380. blobtrans_write(bt, BinarySink_UPCAST(baseossh), extra->base_ossh_fmt);
  381. blobtrans_clear(bt);
  382. if (!success) {
  383. ssh_key_free(&ck->sshk);
  384. strbuf_free(basepub);
  385. strbuf_free(baseossh);
  386. return NULL;
  387. }
  388. strbuf_free(basepub);
  389. { // WINSCP
  390. BinarySource osshsrc[1];
  391. BinarySource_BARE_INIT_PL(osshsrc, ptrlen_from_strbuf(baseossh));
  392. ck->basekey = ssh_key_new_priv_openssh(self->base_alg, osshsrc);
  393. strbuf_free(baseossh);
  394. if (!ck->basekey) {
  395. ssh_key_free(&ck->sshk);
  396. return NULL;
  397. }
  398. return &ck->sshk;
  399. } // WINSCP
  400. } // WINSCP
  401. } // WINSCP
  402. }
  403. static void opensshcert_freekey(ssh_key *key)
  404. {
  405. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  406. /* If this function is called from one of the above constructors
  407. * because it failed part way through, we might not have managed
  408. * to construct ck->basekey, so it might be NULL. */
  409. if (ck->basekey)
  410. ssh_key_free(ck->basekey);
  411. strbuf_free(ck->nonce);
  412. strbuf_free(ck->key_id);
  413. strbuf_free(ck->valid_principals);
  414. strbuf_free(ck->critical_options);
  415. strbuf_free(ck->extensions);
  416. strbuf_free(ck->reserved);
  417. strbuf_free(ck->signature_key);
  418. strbuf_free(ck->signature);
  419. sfree(ck);
  420. }
  421. static ssh_key *opensshcert_base_key(ssh_key *key)
  422. {
  423. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  424. return ck->basekey;
  425. }
  426. /*
  427. * Make a public key object from the CA public blob, potentially
  428. * taking into account that the signature might override the algorithm
  429. * name
  430. */
  431. static ssh_key *opensshcert_ca_pub_key(
  432. opensshcert_key *ck, ptrlen sig, ptrlen *algname)
  433. {
  434. ptrlen ca_keyblob = ptrlen_from_strbuf(ck->signature_key);
  435. ptrlen alg_source = sig.ptr ? sig : ca_keyblob;
  436. if (algname)
  437. *algname = pubkey_blob_to_alg_name(alg_source);
  438. { // WINSCP
  439. const ssh_keyalg *ca_alg = pubkey_blob_to_alg(alg_source);
  440. if (!ca_alg)
  441. return NULL; /* don't even recognise the certifying key type */
  442. return ssh_key_new_pub(ca_alg, ca_keyblob);
  443. } // WINSCP
  444. }
  445. static void opensshcert_signature_preimage(opensshcert_key *ck, BinarySink *bs)
  446. {
  447. const opensshcert_extra *extra = ck->sshk.vt->extra;
  448. put_stringz(bs, extra->cert_key_ssh_id);
  449. put_stringpl(bs, ptrlen_from_strbuf(ck->nonce));
  450. { // WINSCP
  451. strbuf *basepub = strbuf_new();
  452. ssh_key_public_blob(ck->basekey, BinarySink_UPCAST(basepub));
  453. { // WINSCP
  454. BinarySource src[1];
  455. BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(basepub));
  456. get_string(src); /* skip initial key type string */
  457. put_data(bs, get_ptr(src), get_avail(src));
  458. strbuf_free(basepub);
  459. put_uint64(bs, ck->serial);
  460. put_uint32(bs, ck->type);
  461. put_stringpl(bs, ptrlen_from_strbuf(ck->key_id));
  462. put_stringpl(bs, ptrlen_from_strbuf(ck->valid_principals));
  463. put_uint64(bs, ck->valid_after);
  464. put_uint64(bs, ck->valid_before);
  465. put_stringpl(bs, ptrlen_from_strbuf(ck->critical_options));
  466. put_stringpl(bs, ptrlen_from_strbuf(ck->extensions));
  467. put_stringpl(bs, ptrlen_from_strbuf(ck->reserved));
  468. put_stringpl(bs, ptrlen_from_strbuf(ck->signature_key));
  469. } // WINSCP
  470. } // WINSCP
  471. }
  472. static void opensshcert_public_blob(ssh_key *key, BinarySink *bs)
  473. {
  474. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  475. opensshcert_signature_preimage(ck, bs);
  476. put_stringpl(bs, ptrlen_from_strbuf(ck->signature));
  477. }
  478. static void opensshcert_private_blob(ssh_key *key, BinarySink *bs)
  479. {
  480. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  481. ssh_key_private_blob(ck->basekey, bs);
  482. }
  483. static void opensshcert_openssh_blob(ssh_key *key, BinarySink *bs)
  484. {
  485. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  486. const opensshcert_extra *extra = key->vt->extra;
  487. strbuf *cert = strbuf_new();
  488. ssh_key_public_blob(key, BinarySink_UPCAST(cert));
  489. put_stringsb(bs, cert);
  490. { // WINSCP
  491. strbuf *baseossh = strbuf_new_nm();
  492. ssh_key_openssh_blob(ck->basekey, BinarySink_UPCAST(baseossh));
  493. { // WINSCP
  494. BinarySource basesrc[1];
  495. BinarySource_BARE_INIT_PL(basesrc, ptrlen_from_strbuf(baseossh));
  496. { // WINSCP
  497. BLOBTRANS_DECLARE(bt);
  498. blobtrans_read(bt, basesrc, extra->base_ossh_fmt);
  499. blobtrans_write(bt, bs, extra->cert_ossh_fmt);
  500. blobtrans_clear(bt);
  501. strbuf_free(baseossh);
  502. } // WINSCP
  503. } // WINSCP
  504. } // WINSCP
  505. }
  506. static void opensshcert_ca_public_blob(ssh_key *key, BinarySink *bs)
  507. {
  508. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  509. put_datapl(bs, ptrlen_from_strbuf(ck->signature_key));
  510. }
  511. static void opensshcert_cert_id_string(ssh_key *key, BinarySink *bs)
  512. {
  513. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  514. put_datapl(bs, ptrlen_from_strbuf(ck->key_id));
  515. }
  516. static bool opensshcert_has_private(ssh_key *key)
  517. {
  518. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  519. return ssh_key_has_private(ck->basekey);
  520. }
  521. static char *opensshcert_cache_str(ssh_key *key)
  522. {
  523. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  524. return ssh_key_cache_str(ck->basekey);
  525. }
  526. static void opensshcert_time_to_iso8601(BinarySink *bs, uint64_t time)
  527. {
  528. time_t t = time;
  529. char buf[256];
  530. put_data(bs, buf, strftime(buf, sizeof(buf),
  531. "%Y-%m-%d %H:%M:%S UTC", gmtime(&t)));
  532. }
  533. static void opensshcert_string_list_key_components(
  534. key_components *kc, strbuf *input, const char *title, const char *title2)
  535. {
  536. BinarySource src[1];
  537. BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(input));
  538. { // WINSCP
  539. const char *titles[2]; // WINSCP
  540. titles[0] = title;
  541. titles[1] = title2;
  542. { // WINSCP
  543. size_t ntitles = (title2 ? 2 : 1);
  544. unsigned index = 0;
  545. while (get_avail(src)) {
  546. size_t ti; // WINSCP
  547. for (ti = 0; ti < ntitles; ti++) {
  548. ptrlen value = get_string(src);
  549. if (get_err(src))
  550. break;
  551. { // WINSCP
  552. char *name = dupprintf("%s_%u", titles[ti], index);
  553. key_components_add_text_pl(kc, name, value);
  554. sfree(name);
  555. } // WINSCP
  556. }
  557. index++;
  558. }
  559. } // WINSCP
  560. } // WINSCP
  561. }
  562. static key_components *opensshcert_components(ssh_key *key)
  563. {
  564. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  565. key_components *kc = ssh_key_components(ck->basekey);
  566. key_components_add_binary(kc, "cert_nonce", ptrlen_from_strbuf(ck->nonce));
  567. key_components_add_uint(kc, "cert_serial", ck->serial);
  568. switch (ck->type) {
  569. case SSH_CERT_TYPE_HOST:
  570. key_components_add_text(kc, "cert_type", "host");
  571. break;
  572. case SSH_CERT_TYPE_USER:
  573. key_components_add_text(kc, "cert_type", "user");
  574. break;
  575. default:
  576. key_components_add_uint(kc, "cert_type", ck->type);
  577. break;
  578. }
  579. key_components_add_text(kc, "cert_key_id", ck->key_id->s);
  580. opensshcert_string_list_key_components(kc, ck->valid_principals,
  581. "cert_valid_principal", NULL);
  582. key_components_add_uint(kc, "cert_valid_after", ck->valid_after);
  583. key_components_add_uint(kc, "cert_valid_before", ck->valid_before);
  584. /* Translate the validity period into human-legible dates, but
  585. * only if they're not the min/max integer. Rationale: if you see
  586. * "584554051223-11-09 07:00:15 UTC" as the expiry time you'll be
  587. * as likely to think it's a weird buffer overflow as half a
  588. * trillion years in the future! */
  589. if (ck->valid_after != 0) {
  590. strbuf *date = strbuf_new();
  591. opensshcert_time_to_iso8601(BinarySink_UPCAST(date), ck->valid_after);
  592. key_components_add_text_pl(kc, "cert_valid_after_date",
  593. ptrlen_from_strbuf(date));
  594. strbuf_free(date);
  595. }
  596. if (ck->valid_before != 0xFFFFFFFFFFFFFFFFLL ) { // WINSCP
  597. strbuf *date = strbuf_new();
  598. opensshcert_time_to_iso8601(BinarySink_UPCAST(date), ck->valid_before);
  599. key_components_add_text_pl(kc, "cert_valid_before_date",
  600. ptrlen_from_strbuf(date));
  601. strbuf_free(date);
  602. }
  603. opensshcert_string_list_key_components(kc, ck->critical_options,
  604. "cert_critical_option",
  605. "cert_critical_option_data");
  606. opensshcert_string_list_key_components(kc, ck->extensions,
  607. "cert_extension",
  608. "cert_extension_data");
  609. key_components_add_binary(kc, "cert_ca_key", ptrlen_from_strbuf(
  610. ck->signature_key));
  611. { // WINSCP
  612. ptrlen ca_algname;
  613. ssh_key *ca_key = opensshcert_ca_pub_key(ck, make_ptrlen(NULL, 0),
  614. &ca_algname);
  615. key_components_add_text_pl(kc, "cert_ca_key_algorithm_id", ca_algname);
  616. if (ca_key) {
  617. key_components *kc_ca_key = ssh_key_components(ca_key);
  618. size_t i; // WINSCP
  619. for (i = 0; i < kc_ca_key->ncomponents; i++) {
  620. key_component *comp = &kc_ca_key->components[i];
  621. char *subname = dupcat("cert_ca_key_", comp->name);
  622. key_components_add_copy(kc, subname, comp);
  623. sfree(subname);
  624. }
  625. key_components_free(kc_ca_key);
  626. ssh_key_free(ca_key);
  627. }
  628. key_components_add_binary(kc, "cert_ca_sig", ptrlen_from_strbuf(
  629. ck->signature));
  630. return kc;
  631. } // WINSCP
  632. }
  633. static SeatDialogText *opensshcert_cert_info(ssh_key *key)
  634. {
  635. #ifdef WINSCP
  636. assert(false);
  637. return NULL;
  638. #else
  639. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  640. SeatDialogText *text = seat_dialog_text_new();
  641. strbuf *tmp = strbuf_new();
  642. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  643. "Certificate type");
  644. switch (ck->type) {
  645. case SSH_CERT_TYPE_HOST:
  646. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT,
  647. "host key");
  648. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  649. "Valid host names");
  650. break;
  651. case SSH_CERT_TYPE_USER:
  652. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT,
  653. "user authentication key");
  654. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  655. "Valid user names");
  656. break;
  657. default:
  658. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT,
  659. "unknown type %" PRIu32, ck->type);
  660. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  661. "Valid principals");
  662. break;
  663. }
  664. {
  665. BinarySource src[1];
  666. BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(
  667. ck->valid_principals));
  668. { // WINSCP
  669. const char *sep = "";
  670. strbuf_clear(tmp);
  671. while (get_avail(src)) {
  672. ptrlen principal = get_string(src);
  673. if (get_err(src))
  674. break;
  675. put_dataz(tmp, sep);
  676. sep = ",";
  677. put_datapl(tmp, principal);
  678. }
  679. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT,
  680. "%s", tmp->s);
  681. } // WINSCP
  682. }
  683. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  684. "Validity period");
  685. strbuf_clear(tmp);
  686. if (ck->valid_after == 0) {
  687. if (ck->valid_before == 0xFFFFFFFFFFFFFFFFLL) { // WINSCP
  688. put_dataz(tmp, "forever");
  689. } else {
  690. put_dataz(tmp, "until ");
  691. opensshcert_time_to_iso8601(BinarySink_UPCAST(tmp),
  692. ck->valid_before);
  693. }
  694. } else {
  695. if (ck->valid_before == 0xFFFFFFFFFFFFFFFFLL) { // WINSCP
  696. put_dataz(tmp, "after ");
  697. opensshcert_time_to_iso8601(BinarySink_UPCAST(tmp),
  698. ck->valid_after);
  699. } else {
  700. opensshcert_time_to_iso8601(BinarySink_UPCAST(tmp),
  701. ck->valid_after);
  702. put_dataz(tmp, " - ");
  703. opensshcert_time_to_iso8601(BinarySink_UPCAST(tmp),
  704. ck->valid_before);
  705. }
  706. }
  707. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT, "%s", tmp->s);
  708. /*
  709. * List critical options we know about. (This is everything listed
  710. * in PROTOCOL.certkeys that isn't specific to U2F/FIDO key types
  711. * that PuTTY doesn't currently support.)
  712. */
  713. {
  714. BinarySource src[1];
  715. BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(
  716. ck->critical_options));
  717. strbuf_clear(tmp);
  718. while (get_avail(src)) {
  719. ptrlen key = get_string(src);
  720. ptrlen value = get_string(src);
  721. if (get_err(src))
  722. break;
  723. if (ck->type == SSH_CERT_TYPE_USER &&
  724. ptrlen_eq_string(key, "source-address")) {
  725. BinarySource src2[1];
  726. BinarySource_BARE_INIT_PL(src2, value);
  727. { // WINSCP
  728. ptrlen addresslist = get_string(src2);
  729. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  730. "Permitted client IP addresses");
  731. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT,
  732. "%.*s", PTRLEN_PRINTF(addresslist));
  733. } // WINSCP
  734. } else if (ck->type == SSH_CERT_TYPE_USER &&
  735. ptrlen_eq_string(key, "force-command")) {
  736. BinarySource src2[1];
  737. BinarySource_BARE_INIT_PL(src2, value);
  738. { // WINSCP
  739. ptrlen command = get_string(src2);
  740. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  741. "Forced remote command");
  742. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT,
  743. "%.*s", PTRLEN_PRINTF(command));
  744. } // WINSCP
  745. }
  746. }
  747. }
  748. /*
  749. * List certificate extensions. Again, we go through everything in
  750. * PROTOCOL.certkeys that isn't specific to U2F/FIDO key types.
  751. * But we also flip the sense round for user-readability: I think
  752. * it's more likely that the typical key will permit all these
  753. * things, so we emit no output in that case, and only mention the
  754. * things that _aren't_ enabled.
  755. */
  756. { // WINSCP
  757. bool x11_ok = false, agent_ok = false, portfwd_ok = false;
  758. bool pty_ok = false, user_rc_ok = false;
  759. {
  760. BinarySource src[1];
  761. BinarySource_BARE_INIT_PL(src, ptrlen_from_strbuf(
  762. ck->extensions));
  763. while (get_avail(src)) {
  764. ptrlen key = get_string(src);
  765. /* ptrlen value = */ get_string(src); // nothing needs this yet
  766. if (get_err(src))
  767. break;
  768. if (ptrlen_eq_string(key, "permit-X11-forwarding")) {
  769. x11_ok = true;
  770. } else if (ptrlen_eq_string(key, "permit-agent-forwarding")) {
  771. agent_ok = true;
  772. } else if (ptrlen_eq_string(key, "permit-port-forwarding")) {
  773. portfwd_ok = true;
  774. } else if (ptrlen_eq_string(key, "permit-pty")) {
  775. pty_ok = true;
  776. } else if (ptrlen_eq_string(key, "permit-user-rc")) {
  777. user_rc_ok = true;
  778. }
  779. }
  780. }
  781. if (ck->type == SSH_CERT_TYPE_USER) {
  782. if (!x11_ok) {
  783. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  784. "X11 forwarding permitted");
  785. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT, "no");
  786. }
  787. if (!agent_ok) {
  788. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  789. "Agent forwarding permitted");
  790. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT, "no");
  791. }
  792. if (!portfwd_ok) {
  793. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  794. "Port forwarding permitted");
  795. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT, "no");
  796. }
  797. if (!pty_ok) {
  798. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  799. "PTY allocation permitted");
  800. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT, "no");
  801. }
  802. if (!user_rc_ok) {
  803. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  804. "Running user ~/.ssh.rc permitted");
  805. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT, "no");
  806. }
  807. }
  808. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  809. "Certificate ID string");
  810. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT,
  811. "%s", ck->key_id->s);
  812. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  813. "Certificate serial number");
  814. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT,
  815. "%" PRIu64, ck->serial);
  816. { // WINSCP
  817. char *fp = ssh2_fingerprint_blob(ptrlen_from_strbuf(ck->signature_key),
  818. SSH_FPTYPE_DEFAULT);
  819. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  820. "Fingerprint of signing CA key");
  821. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT, "%s", fp);
  822. sfree(fp);
  823. fp = ssh2_fingerprint(key, ssh_fptype_to_cert(SSH_FPTYPE_DEFAULT));
  824. seat_dialog_text_append(text, SDT_MORE_INFO_KEY,
  825. "Fingerprint including certificate");
  826. seat_dialog_text_append(text, SDT_MORE_INFO_VALUE_SHORT, "%s", fp);
  827. sfree(fp);
  828. strbuf_free(tmp);
  829. return text;
  830. } // WINSCP
  831. } // WINSCP
  832. #endif
  833. }
  834. static int opensshcert_pubkey_bits(const ssh_keyalg *self, ptrlen blob)
  835. {
  836. BinarySource src[1];
  837. BinarySource_BARE_INIT_PL(src, blob);
  838. get_string(src); /* key type */
  839. get_string(src); /* nonce */
  840. { // WINSCP
  841. strbuf *basepub = get_base_public_blob(src, self->extra);
  842. int bits = ssh_key_public_bits(
  843. self->base_alg, ptrlen_from_strbuf(basepub));
  844. strbuf_free(basepub);
  845. return bits;
  846. } // WINSCP
  847. }
  848. static unsigned opensshcert_supported_flags(const ssh_keyalg *self)
  849. {
  850. return ssh_keyalg_supported_flags(self->base_alg);
  851. }
  852. static const char *opensshcert_alternate_ssh_id(const ssh_keyalg *self,
  853. unsigned flags)
  854. {
  855. const char *base_id = ssh_keyalg_alternate_ssh_id(self->base_alg, flags);
  856. size_t i; // WINSCP
  857. for (i = 0; i < lenof(opensshcert_all_keyalgs); i++) {
  858. const ssh_keyalg *alg_i = opensshcert_all_keyalgs[i];
  859. if (!strcmp(base_id, alg_i->base_alg->ssh_id))
  860. return alg_i->ssh_id;
  861. }
  862. return self->ssh_id;
  863. }
  864. static char *opensshcert_alg_desc(const ssh_keyalg *self)
  865. {
  866. char *base_desc = ssh_keyalg_desc(self->base_alg);
  867. char *our_desc = dupcat(base_desc, " cert");
  868. sfree(base_desc);
  869. return our_desc;
  870. }
  871. static bool opensshcert_variable_size(const ssh_keyalg *self)
  872. {
  873. return ssh_keyalg_variable_size(self->base_alg);
  874. }
  875. static const ssh_keyalg *opensshcert_related_alg(const ssh_keyalg *self,
  876. const ssh_keyalg *base)
  877. {
  878. size_t i; // WINSCP
  879. for (i = 0; i < lenof(opensshcert_all_keyalgs); i++) {
  880. const ssh_keyalg *alg_i = opensshcert_all_keyalgs[i];
  881. if (base == alg_i->base_alg)
  882. return alg_i;
  883. }
  884. return self;
  885. }
  886. static char *opensshcert_invalid(ssh_key *key, unsigned flags)
  887. {
  888. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  889. return ssh_key_invalid(ck->basekey, flags);
  890. }
  891. static bool opensshcert_check_cert(
  892. ssh_key *key, bool host, ptrlen principal, uint64_t time,
  893. const ca_options *opts, BinarySink *error)
  894. {
  895. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  896. bool result = false;
  897. ssh_key *ca_key = NULL;
  898. strbuf *preimage = strbuf_new();
  899. BinarySource src[1];
  900. ptrlen signature = ptrlen_from_strbuf(ck->signature);
  901. /*
  902. * The OpenSSH certificate spec is one-layer only: it explicitly
  903. * forbids using a certified key in turn as the CA.
  904. *
  905. * If it did not, then we'd also have to recursively verify
  906. * everything up the CA chain until we reached the ultimate root,
  907. * and then make sure _that_ was something we trusted. (Not to
  908. * mention that there'd probably be an additional SSH_CERT_TYPE_CA
  909. * or some such, and certificate options saying what kinds of
  910. * certificate a CA was trusted to sign for, and ...)
  911. */
  912. ca_key = opensshcert_ca_pub_key(ck, make_ptrlen(NULL, 0), NULL);
  913. if (!ca_key) {
  914. put_fmt(error, "Certificate's signing key is invalid");
  915. goto out;
  916. }
  917. if (ssh_key_alg(ca_key)->is_certificate) {
  918. put_fmt(error, "Certificate is signed with a certified key "
  919. "(forbidden by OpenSSH certificate specification)");
  920. goto out;
  921. }
  922. /*
  923. * Now re-instantiate the key in a way that matches the signature
  924. * (i.e. so that if the key is an RSA one we get the right subtype
  925. * of RSA).
  926. */
  927. ssh_key_free(ca_key);
  928. ca_key = opensshcert_ca_pub_key(ck, signature, NULL);
  929. if (!ca_key) {
  930. put_fmt(error, "Certificate's signing key does not match "
  931. "signature type");
  932. goto out;
  933. }
  934. /* Check which signature algorithm is actually in use, because
  935. * that might be a reason to reject the certificate (e.g. ssh-rsa
  936. * when we wanted rsa-sha2-*). */
  937. { // WINSCP
  938. const ssh_keyalg *sig_alg = ssh_key_alg(ca_key);
  939. if ((sig_alg == &ssh_rsa && !opts->permit_rsa_sha1) ||
  940. (sig_alg == &ssh_rsa_sha256 && !opts->permit_rsa_sha256) ||
  941. (sig_alg == &ssh_rsa_sha512 && !opts->permit_rsa_sha512)) {
  942. put_fmt(error, "Certificate signature uses '%s' signature type "
  943. "(forbidden by user configuration)", sig_alg->ssh_id);
  944. goto out;
  945. }
  946. opensshcert_signature_preimage(ck, BinarySink_UPCAST(preimage));
  947. if (!ssh_key_verify(ca_key, signature, ptrlen_from_strbuf(preimage))) {
  948. put_fmt(error, "Certificate's signature is invalid");
  949. goto out;
  950. }
  951. { // WINSCP
  952. uint32_t expected_type = host ? SSH_CERT_TYPE_HOST : SSH_CERT_TYPE_USER;
  953. if (ck->type != expected_type) {
  954. put_fmt(error, "Certificate type is ");
  955. switch (ck->type) {
  956. case SSH_CERT_TYPE_HOST:
  957. put_fmt(error, "host");
  958. break;
  959. case SSH_CERT_TYPE_USER:
  960. put_fmt(error, "user");
  961. break;
  962. default:
  963. put_fmt(error, "unknown value %" PRIu32, ck->type);
  964. break;
  965. }
  966. put_fmt(error, "; expected %s", host ? "host" : "user");
  967. goto out;
  968. }
  969. /*
  970. * Check the time bounds on the certificate.
  971. */
  972. if (time < ck->valid_after) {
  973. put_fmt(error, "Certificate is not valid until ");
  974. opensshcert_time_to_iso8601(BinarySink_UPCAST(error),
  975. ck->valid_after);
  976. goto out;
  977. }
  978. if (time >= ck->valid_before) {
  979. put_fmt(error, "Certificate expired at ");
  980. opensshcert_time_to_iso8601(BinarySink_UPCAST(error),
  981. ck->valid_before);
  982. goto out;
  983. }
  984. /*
  985. * Check that this certificate is for the right thing.
  986. *
  987. * If valid_principals is a zero-length string then this is
  988. * specified to be a carte-blanche certificate valid for any
  989. * principal (at least, provided you trust the CA that issued it).
  990. */
  991. if (ck->valid_principals->len != 0) {
  992. BinarySource_BARE_INIT_PL(
  993. src, ptrlen_from_strbuf(ck->valid_principals));
  994. while (get_avail(src)) {
  995. ptrlen valid_principal = get_string(src);
  996. if (get_err(src)) {
  997. put_fmt(error, "Certificate's valid principals list is "
  998. "incorrectly formatted");
  999. goto out;
  1000. }
  1001. if (ptrlen_eq_ptrlen(valid_principal, principal))
  1002. goto principal_ok;
  1003. }
  1004. /*
  1005. * No valid principal matched. Now go through the list a
  1006. * second time writing the cert contents into the error
  1007. * message, so that the user can see at a glance what went
  1008. * wrong.
  1009. *
  1010. * (If you've typed the wrong spelling of the host name, you
  1011. * really need to see "This cert is for 'foo.example.com' and
  1012. * I was trying to match it against 'foo'", rather than just
  1013. * "Computer says no".)
  1014. */
  1015. put_fmt(error, "Certificate's %s list [",
  1016. host ? "hostname" : "username");
  1017. BinarySource_BARE_INIT_PL(
  1018. src, ptrlen_from_strbuf(ck->valid_principals));
  1019. { // WINSCP
  1020. const char *sep = "";
  1021. while (get_avail(src)) {
  1022. ptrlen valid_principal = get_string(src);
  1023. put_fmt(error, "%s\"", sep);
  1024. put_c_string_literal(error, valid_principal);
  1025. put_fmt(error, "\"");
  1026. sep = ", ";
  1027. }
  1028. put_fmt(error, "] does not contain expected %s \"",
  1029. host ? "hostname" : "username");
  1030. put_c_string_literal(error, principal);
  1031. put_fmt(error, "\"");
  1032. goto out;
  1033. principal_ok:;
  1034. } // WINSCP
  1035. }
  1036. /*
  1037. * Check for critical options.
  1038. */
  1039. {
  1040. BinarySource_BARE_INIT_PL(
  1041. src, ptrlen_from_strbuf(ck->critical_options));
  1042. while (get_avail(src)) {
  1043. ptrlen option = get_string(src);
  1044. ptrlen data = get_string(src);
  1045. if (get_err(src)) {
  1046. put_fmt(error, "Certificate's critical options list is "
  1047. "incorrectly formatted");
  1048. goto out;
  1049. }
  1050. /*
  1051. * If we ever do support any options, this will be where
  1052. * we insert code to recognise and validate them.
  1053. *
  1054. * At present, we implement no critical options at all.
  1055. * (For host certs, as of 2022-04-20, OpenSSH hasn't
  1056. * defined any. For user certs, the only SSH server using
  1057. * this is Uppity, which doesn't support key restrictions
  1058. * in general.)
  1059. */
  1060. (void)data; /* no options supported => no use made of the data */
  1061. /*
  1062. * Report an unrecognised literal.
  1063. */
  1064. put_fmt(error, "Certificate specifies an unsupported critical "
  1065. "option \"");
  1066. put_c_string_literal(error, option);
  1067. put_fmt(error, "\"");
  1068. goto out;
  1069. }
  1070. }
  1071. /* If we get here without failing any check, accept the certificate! */
  1072. result = true;
  1073. out:
  1074. if (ca_key)
  1075. ssh_key_free(ca_key);
  1076. strbuf_free(preimage);
  1077. return result;
  1078. } // WINSCP
  1079. } // WINSCP
  1080. }
  1081. static bool opensshcert_verify(ssh_key *key, ptrlen sig, ptrlen data)
  1082. {
  1083. /* This method is pure *signature* verification; checking the
  1084. * certificate is done elsewhere. */
  1085. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  1086. return ssh_key_verify(ck->basekey, sig, data);
  1087. }
  1088. static void opensshcert_sign(ssh_key *key, ptrlen data, unsigned flags,
  1089. BinarySink *bs)
  1090. {
  1091. opensshcert_key *ck = container_of(key, opensshcert_key, sshk);
  1092. ssh_key_sign(ck->basekey, data, flags, bs);
  1093. }