You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

287 lines
8.5 KiB

  1. /* Copyright (c) 2014, Google Inc.
  2. *
  3. * Permission to use, copy, modify, and/or distribute this software for any
  4. * purpose with or without fee is hereby granted, provided that the above
  5. * copyright notice and this permission notice appear in all copies.
  6. *
  7. * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
  8. * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
  9. * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
  10. * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
  11. * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
  12. * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
  13. * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */
  14. #include <openssl/base.h>
  15. #include <openssl/err.h>
  16. #include <openssl/pem.h>
  17. #include <openssl/ssl.h>
  18. #include "../crypto/test/scoped_types.h"
  19. #include "../ssl/test/scoped_types.h"
  20. #include "internal.h"
  21. #include "transport_common.h"
  22. static const struct argument kArguments[] = {
  23. {
  24. "-connect", kRequiredArgument,
  25. "The hostname and port of the server to connect to, e.g. foo.com:443",
  26. },
  27. {
  28. "-cipher", kOptionalArgument,
  29. "An OpenSSL-style cipher suite string that configures the offered ciphers",
  30. },
  31. {
  32. "-max-version", kOptionalArgument,
  33. "The maximum acceptable protocol version",
  34. },
  35. {
  36. "-min-version", kOptionalArgument,
  37. "The minimum acceptable protocol version",
  38. },
  39. {
  40. "-server-name", kOptionalArgument,
  41. "The server name to advertise",
  42. },
  43. {
  44. "-select-next-proto", kOptionalArgument,
  45. "An NPN protocol to select if the server supports NPN",
  46. },
  47. {
  48. "-alpn-protos", kOptionalArgument,
  49. "A comma-separated list of ALPN protocols to advertise",
  50. },
  51. {
  52. "-fallback-scsv", kBooleanArgument,
  53. "Enable FALLBACK_SCSV",
  54. },
  55. {
  56. "-ocsp-stapling", kBooleanArgument,
  57. "Advertise support for OCSP stabling",
  58. },
  59. {
  60. "-signed-certificate-timestamps", kBooleanArgument,
  61. "Advertise support for signed certificate timestamps",
  62. },
  63. {
  64. "-channel-id-key", kOptionalArgument,
  65. "The key to use for signing a channel ID",
  66. },
  67. {
  68. "-false-start", kBooleanArgument,
  69. "Enable False Start",
  70. },
  71. { "-session-in", kOptionalArgument,
  72. "A file containing a session to resume.",
  73. },
  74. { "-session-out", kOptionalArgument,
  75. "A file to write the negotiated session to.",
  76. },
  77. {
  78. "", kOptionalArgument, "",
  79. },
  80. };
  81. static ScopedEVP_PKEY LoadPrivateKey(const std::string &file) {
  82. ScopedBIO bio(BIO_new(BIO_s_file()));
  83. if (!bio || !BIO_read_filename(bio.get(), file.c_str())) {
  84. return nullptr;
  85. }
  86. ScopedEVP_PKEY pkey(PEM_read_bio_PrivateKey(bio.get(), nullptr, nullptr,
  87. nullptr));
  88. return pkey;
  89. }
  90. static bool VersionFromString(uint16_t *out_version,
  91. const std::string& version) {
  92. if (version == "ssl3") {
  93. *out_version = SSL3_VERSION;
  94. return true;
  95. } else if (version == "tls1" || version == "tls1.0") {
  96. *out_version = TLS1_VERSION;
  97. return true;
  98. } else if (version == "tls1.1") {
  99. *out_version = TLS1_1_VERSION;
  100. return true;
  101. } else if (version == "tls1.2") {
  102. *out_version = TLS1_2_VERSION;
  103. return true;
  104. }
  105. return false;
  106. }
  107. static int NextProtoSelectCallback(SSL* ssl, uint8_t** out, uint8_t* outlen,
  108. const uint8_t* in, unsigned inlen, void* arg) {
  109. *out = reinterpret_cast<uint8_t *>(arg);
  110. *outlen = strlen(reinterpret_cast<const char *>(arg));
  111. return SSL_TLSEXT_ERR_OK;
  112. }
  113. bool Client(const std::vector<std::string> &args) {
  114. if (!InitSocketLibrary()) {
  115. return false;
  116. }
  117. std::map<std::string, std::string> args_map;
  118. if (!ParseKeyValueArguments(&args_map, args, kArguments)) {
  119. PrintUsage(kArguments);
  120. return false;
  121. }
  122. ScopedSSL_CTX ctx(SSL_CTX_new(SSLv23_client_method()));
  123. const char *keylog_file = getenv("SSLKEYLOGFILE");
  124. if (keylog_file) {
  125. BIO *keylog_bio = BIO_new_file(keylog_file, "a");
  126. if (!keylog_bio) {
  127. ERR_print_errors_cb(PrintErrorCallback, stderr);
  128. return false;
  129. }
  130. SSL_CTX_set_keylog_bio(ctx.get(), keylog_bio);
  131. }
  132. if (args_map.count("-cipher") != 0 &&
  133. !SSL_CTX_set_cipher_list(ctx.get(), args_map["-cipher"].c_str())) {
  134. fprintf(stderr, "Failed setting cipher list\n");
  135. return false;
  136. }
  137. if (args_map.count("-max-version") != 0) {
  138. uint16_t version;
  139. if (!VersionFromString(&version, args_map["-max-version"])) {
  140. fprintf(stderr, "Unknown protocol version: '%s'\n",
  141. args_map["-max-version"].c_str());
  142. return false;
  143. }
  144. SSL_CTX_set_max_version(ctx.get(), version);
  145. }
  146. if (args_map.count("-min-version") != 0) {
  147. uint16_t version;
  148. if (!VersionFromString(&version, args_map["-min-version"])) {
  149. fprintf(stderr, "Unknown protocol version: '%s'\n",
  150. args_map["-min-version"].c_str());
  151. return false;
  152. }
  153. SSL_CTX_set_min_version(ctx.get(), version);
  154. }
  155. if (args_map.count("-select-next-proto") != 0) {
  156. const std::string &proto = args_map["-select-next-proto"];
  157. if (proto.size() > 255) {
  158. fprintf(stderr, "Bad NPN protocol: '%s'\n", proto.c_str());
  159. return false;
  160. }
  161. // |SSL_CTX_set_next_proto_select_cb| is not const-correct.
  162. SSL_CTX_set_next_proto_select_cb(ctx.get(), NextProtoSelectCallback,
  163. const_cast<char *>(proto.c_str()));
  164. }
  165. if (args_map.count("-alpn-protos") != 0) {
  166. const std::string &alpn_protos = args_map["-alpn-protos"];
  167. std::vector<uint8_t> wire;
  168. size_t i = 0;
  169. while (i <= alpn_protos.size()) {
  170. size_t j = alpn_protos.find(',', i);
  171. if (j == std::string::npos) {
  172. j = alpn_protos.size();
  173. }
  174. size_t len = j - i;
  175. if (len > 255) {
  176. fprintf(stderr, "Invalid ALPN protocols: '%s'\n", alpn_protos.c_str());
  177. return false;
  178. }
  179. wire.push_back(static_cast<uint8_t>(len));
  180. wire.resize(wire.size() + len);
  181. memcpy(wire.data() + wire.size() - len, alpn_protos.data() + i, len);
  182. i = j + 1;
  183. }
  184. if (SSL_CTX_set_alpn_protos(ctx.get(), wire.data(), wire.size()) != 0) {
  185. return false;
  186. }
  187. }
  188. if (args_map.count("-fallback-scsv") != 0) {
  189. SSL_CTX_set_mode(ctx.get(), SSL_MODE_SEND_FALLBACK_SCSV);
  190. }
  191. if (args_map.count("-ocsp-stapling") != 0) {
  192. SSL_CTX_enable_ocsp_stapling(ctx.get());
  193. }
  194. if (args_map.count("-signed-certificate-timestamps") != 0) {
  195. SSL_CTX_enable_signed_cert_timestamps(ctx.get());
  196. }
  197. if (args_map.count("-channel-id-key") != 0) {
  198. ScopedEVP_PKEY pkey = LoadPrivateKey(args_map["-channel-id-key"]);
  199. if (!pkey || !SSL_CTX_set1_tls_channel_id(ctx.get(), pkey.get())) {
  200. return false;
  201. }
  202. }
  203. if (args_map.count("-false-start") != 0) {
  204. SSL_CTX_set_mode(ctx.get(), SSL_MODE_ENABLE_FALSE_START);
  205. }
  206. int sock = -1;
  207. if (!Connect(&sock, args_map["-connect"])) {
  208. return false;
  209. }
  210. ScopedBIO bio(BIO_new_socket(sock, BIO_CLOSE));
  211. ScopedSSL ssl(SSL_new(ctx.get()));
  212. if (args_map.count("-server-name") != 0) {
  213. SSL_set_tlsext_host_name(ssl.get(), args_map["-server-name"].c_str());
  214. }
  215. if (args_map.count("-session-in") != 0) {
  216. ScopedBIO in(BIO_new_file(args_map["-session-in"].c_str(), "rb"));
  217. if (!in) {
  218. fprintf(stderr, "Error reading session\n");
  219. ERR_print_errors_cb(PrintErrorCallback, stderr);
  220. return false;
  221. }
  222. ScopedSSL_SESSION session(PEM_read_bio_SSL_SESSION(in.get(), nullptr,
  223. nullptr, nullptr));
  224. if (!session) {
  225. fprintf(stderr, "Error reading session\n");
  226. ERR_print_errors_cb(PrintErrorCallback, stderr);
  227. return false;
  228. }
  229. SSL_set_session(ssl.get(), session.get());
  230. }
  231. SSL_set_bio(ssl.get(), bio.get(), bio.get());
  232. bio.release();
  233. int ret = SSL_connect(ssl.get());
  234. if (ret != 1) {
  235. int ssl_err = SSL_get_error(ssl.get(), ret);
  236. fprintf(stderr, "Error while connecting: %d\n", ssl_err);
  237. ERR_print_errors_cb(PrintErrorCallback, stderr);
  238. return false;
  239. }
  240. fprintf(stderr, "Connected.\n");
  241. PrintConnectionInfo(ssl.get());
  242. if (args_map.count("-session-out") != 0) {
  243. ScopedBIO out(BIO_new_file(args_map["-session-out"].c_str(), "wb"));
  244. if (!out ||
  245. !PEM_write_bio_SSL_SESSION(out.get(), SSL_get0_session(ssl.get()))) {
  246. fprintf(stderr, "Error while saving session:\n");
  247. ERR_print_errors_cb(PrintErrorCallback, stderr);
  248. return false;
  249. }
  250. }
  251. bool ok = TransferData(ssl.get(), sock);
  252. return ok;
  253. }