/* $Id: sslgetpage.C,v 1.2 2000/04/24 00:42:51 fubob Exp $ */ /* * * Copyright (C) 2000 Michael Kaminsky (kaminsky@lcs.mit.edu) * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2, or (at * your option) any later version. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 * USA * */ #include "async.h" #include "parseopt.h" #include "rxx.h" #include #include #include #define PublicKey_digest(data, type, md, len) \ ASN1_digest ((int (*) ()) i2d_PublicKey, type, (char *) data, md, len) const int SSL_VERSION = 3; const int FORMAT_UNDEF = 0; const int FORMAT_ASN1 = 1; const int FORMAT_TEXT = 2; const int FORMAT_PEM = 3; const int FORMAT_NETSCAPE = 4; const char CERT_HDR[] = "certificate"; str hostname; str hostkey; int hostport; bool opt_verbose = false; in_addr ia; static void usage () { fatal << "usage: " << progname << " [-v] https-server-name\n"; } static const str SSLrx = "^([\\w\\.-]+)(:\\d{1,6})?:([A-KMNP-Za-kmnp-z2-9]{32})\\S*"; static bool parseurl (str arg, str *name, int *port, str *key) { rxx r (SSLrx); if (!r.match (arg)) return false; *name = r[1]; if (r[2]) convertint (r[2].cstr () + 1, port); // skip colon else *port = 443; *key = r[3]; return true; } void make_ssl_connection (int sock); void connectagain () { tcpconnect (ia, hostport, wrap (make_ssl_connection)); } void make_ssl_connection (int sock) { if (sock < 0) { timecb (timenow +1, wrap (connectagain)); return; } make_sync (sock); SSL_METHOD *req_method; SSL_CTX* ctx; SSL* ssl; int err; #if 0 X509* server_cert; char *s; #endif /* global setup */ SSL_load_error_strings (); /* Setup all the global SSL stuff */ SSLeay_add_ssl_algorithms (); switch (SSL_VERSION) { default: req_method = SSLv23_client_method (); break; case 2: req_method = SSLv2_client_method (); break; case 3: req_method = SSLv3_client_method (); break; } ctx = SSL_CTX_new (req_method); if (!ctx) fatal ("SSL: couldn't create a context!\n"); /* Lets make an SSL structure */ ssl = SSL_new (ctx); if (!ssl) fatal ("SSL_new: failed\n"); SSL_set_connect_state (ssl); /* pass the raw socket into the SSL layers */ SSL_set_fd (ssl, sock); err = SSL_connect (ssl); if (err == -1) { err = ERR_get_error (); fatal ("SSL: %s\n", ERR_error_string (err, NULL)); } if (opt_verbose) warnx ("SSL connection using %s\n", SSL_get_cipher (ssl)); /* Get server's certificate (note: beware of dynamic allocation) - opt */ /* major serious hack alert -- we should check certificates * to authenticate the server; otherwise we risk man-in-the-middle * attack */ #if 0 server_cert = SSL_get_peer_certificate (ssl); if (!server_cert) fatal ("SSL: couldn't get peer certificate!\n"); if (opt_verbose) warnx << "Server certificate:\n"; s = X509_NAME_oneline (X509_get_subject_name (server_cert), 0, 0); if (!server_cert) fatal ("SSL: couldn't get X509-subject!\n"); if (opt_verbose) warnx ("\t subject: %s\n", s); free (s); s = X509_NAME_oneline (X509_get_issuer_name (server_cert), 0, 0); if (!server_cert) fatal ("SSL: couldn't get X509-issuer name!\n"); if (opt_verbose) warnx ("\t issuer: %s\n", s); free (s); /* We could do all sorts of certificate verification stuff here before deallocating the certificate. */ unsigned int n; unsigned char md[EVP_MAX_MD_SIZE]; EVP_PKEY *pk = X509_get_pubkey (server_cert); if (!PublicKey_digest (pk, EVP_sha1 (), md, &n)) fatal << "PublicKey digest: out of memory\n"; str pk_sha1 = armor32 (md, n); if (opt_verbose) warnx << "\t SHA1 Public Key Fingerprint = " << pk_sha1 << "\n"; EVP_PKEY_free (pk); X509_free (server_cert); if (pk_sha1 != hostkey) fatal << "Given hostkey does not match one returned by server\n"; #endif /* XXX: write header to server and fetch the page */ char buf[4096]; strcpy (buf, "HEAD / HTTP/1.0\r\n\r\n"); err = strlen (buf); #if 0 while ((err = read (0, buf, sizeof (buf))) > 0) { if (err == -1) fatal << "read: " << strerror (errno) << "\n"; warn << str(buf, err) << err << "\n"; #endif err = SSL_write (ssl, buf, err); if (err == -1) { err = ERR_get_error (); fatal ("SSL_write: %s\n", ERR_error_string (err, NULL)); } // } while ((err = SSL_read (ssl, buf, sizeof (buf))) > 0) { if (err == -1) { err = ERR_get_error (); fatal ("SSL_read: %s\n", ERR_error_string (err, NULL)); } err = write (1, buf, err); if (err == -1) fatal << "write: " << strerror (errno) << "\n"; } SSL_shutdown (ssl); /* send SSL/TLS close_notify */ /* Clean up. */ close (sock); SSL_free (ssl); SSL_CTX_free (ctx); exit (0); } int main (int argc, char **argv) { setprogname (argv[0]); int ch; while ((ch = getopt (argc, argv, "v")) != -1) switch (ch) { case 'v': opt_verbose = true; break; default: usage (); } argc -= optind; argv += optind; if (argc < 1) usage (); // warn << argv[0] << "\n"; if (!parseurl (argv[0], &hostname, &hostport, &hostkey)) fatal << "Could not parse argument--should be of the form\n" << "\thostname:sslkeyhash.ssl\n"; hostport = 8443; #if 0 struct hostent *h; if ((h = gethostbyname (hostname)) == NULL) { warn << "gethostbyname failed\n"; exit(-1); } ia = (struct in_addr *) h->h_addr; #endif ia.s_addr = inet_addr("18.26.4.31"); tcpconnect (ia, hostport, wrap (make_ssl_connection)); amain (); }