Skip to content
s_client.c 46.3 KiB
Newer Older
		SSL_set_tlsext_debug_callback(con, tlsext_cb);
		SSL_set_tlsext_debug_arg(con, bio_c_out);
		}
	if (c_status_req)
		{
		SSL_set_tlsext_status_type(con, TLSEXT_STATUSTYPE_ocsp);
		SSL_CTX_set_tlsext_status_cb(ctx, ocsp_resp_cb);
		SSL_CTX_set_tlsext_status_arg(ctx, bio_c_out);
#if 0
{
STACK_OF(OCSP_RESPID) *ids = sk_OCSP_RESPID_new_null();
OCSP_RESPID *id = OCSP_RESPID_new();
id->value.byKey = ASN1_OCTET_STRING_new();
id->type = V_OCSP_RESPID_KEY;
ASN1_STRING_set(id->value.byKey, "Hello World", -1);
sk_OCSP_RESPID_push(ids, id);
SSL_set_tlsext_status_ids(con, ids);
}
#endif
		}

	SSL_set_bio(con,sbio,sbio);
	SSL_set_connect_state(con);

	/* ok, lets connect */
	width=SSL_get_fd(con)+1;

	read_tty=1;
	write_tty=0;
	tty_on=0;
	read_ssl=1;
	write_ssl=1;
	
	cbuf_len=0;
	cbuf_off=0;
	sbuf_len=0;
	sbuf_off=0;

	/* This is an ugly hack that does a lot of assumptions */
	/* We do have to handle multi-line responses which may come
 	   in a single packet or not. We therefore have to use
	   BIO_gets() which does need a buffering BIO. So during
	   the initial chitchat we do push a buffering BIO into the
	   chain that is removed again later on to not disturb the
	   rest of the s_client operation. */
	if (starttls_proto == PROTO_SMTP)
		BIO *fbio = BIO_new(BIO_f_buffer());
		BIO_push(fbio, sbio);
		/* wait for multi-line response to end from SMTP */
		do
			{
			mbuf_len = BIO_gets(fbio,mbuf,BUFSIZZ);
			}
		while (mbuf_len>3 && mbuf[3]=='-');
		/* STARTTLS command requires EHLO... */
		BIO_printf(fbio,"EHLO openssl.client.net\r\n");
Dr. Stephen Henson's avatar
Dr. Stephen Henson committed
		(void)BIO_flush(fbio);
		/* wait for multi-line response to end EHLO SMTP response */
		do
			{
			mbuf_len = BIO_gets(fbio,mbuf,BUFSIZZ);
			if (strstr(mbuf,"STARTTLS"))
				foundit=1;
			}
		while (mbuf_len>3 && mbuf[3]=='-');
Dr. Stephen Henson's avatar
Dr. Stephen Henson committed
		(void)BIO_flush(fbio);
		if (!foundit)
			BIO_printf(bio_err,
				   "didn't found starttls in server response,"
				   " try anyway...\n");
		BIO_printf(sbio,"STARTTLS\r\n");
		BIO_read(sbio,sbuf,BUFSIZZ);
		}
	else if (starttls_proto == PROTO_POP3)
		{
		BIO_read(sbio,mbuf,BUFSIZZ);
		BIO_printf(sbio,"STLS\r\n");
		BIO_read(sbio,sbuf,BUFSIZZ);
		}
	else if (starttls_proto == PROTO_IMAP)
		{
		BIO *fbio = BIO_new(BIO_f_buffer());
		BIO_push(fbio, sbio);
		BIO_gets(fbio,mbuf,BUFSIZZ);
		/* STARTTLS command requires CAPABILITY... */
		BIO_printf(fbio,". CAPABILITY\r\n");
Dr. Stephen Henson's avatar
Dr. Stephen Henson committed
		(void)BIO_flush(fbio);
		/* wait for multi-line CAPABILITY response */
		do
			{
			mbuf_len = BIO_gets(fbio,mbuf,BUFSIZZ);
			if (strstr(mbuf,"STARTTLS"))
				foundit=1;
			}
		while (mbuf_len>3 && mbuf[0]!='.');
Dr. Stephen Henson's avatar
Dr. Stephen Henson committed
		(void)BIO_flush(fbio);
		if (!foundit)
			BIO_printf(bio_err,
				   "didn't found STARTTLS in server response,"
				   " try anyway...\n");
		BIO_printf(sbio,". STARTTLS\r\n");
		BIO_read(sbio,sbuf,BUFSIZZ);
		}
	else if (starttls_proto == PROTO_FTP)
		{
		BIO *fbio = BIO_new(BIO_f_buffer());
		BIO_push(fbio, sbio);
		/* wait for multi-line response to end from FTP */
		do
			{
			mbuf_len = BIO_gets(fbio,mbuf,BUFSIZZ);
			}
		while (mbuf_len>3 && mbuf[3]=='-');
Dr. Stephen Henson's avatar
Dr. Stephen Henson committed
		(void)BIO_flush(fbio);
		BIO_printf(sbio,"AUTH TLS\r\n");
		BIO_read(sbio,sbuf,BUFSIZZ);
		}
Ben Laurie's avatar
Ben Laurie committed
	if (starttls_proto == PROTO_XMPP)
		{
		int seen = 0;
		BIO_printf(sbio,"<stream:stream "
		    "xmlns:stream='http://etherx.jabber.org/streams' "
		    "xmlns='jabber:client' to='%s' version='1.0'>", host);
		seen = BIO_read(sbio,mbuf,BUFSIZZ);
		mbuf[seen] = 0;
		while (!strstr(mbuf, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'"))
			{
			if (strstr(mbuf, "/stream:features>"))
				goto shut;
			seen = BIO_read(sbio,mbuf,BUFSIZZ);
			mbuf[seen] = 0;
			}
		BIO_printf(sbio, "<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
		seen = BIO_read(sbio,sbuf,BUFSIZZ);
		sbuf[seen] = 0;
		if (!strstr(sbuf, "<proceed"))
			goto shut;
		mbuf[0] = 0;
		}
	for (;;)
		{
		FD_ZERO(&readfds);
		FD_ZERO(&writefds);

		if (SSL_in_init(con) && !SSL_total_renegotiations(con))
			{
			in_init=1;
			tty_on=0;
			}
		else
			{
			tty_on=1;
			if (in_init)
				{
				in_init=0;
#if 0 /* This test doesn't really work as intended (needs to be fixed) */
#ifndef OPENSSL_NO_TLSEXT
				if (servername != NULL && !SSL_session_reused(con))
					{
					BIO_printf(bio_c_out,"Server did %sacknowledge servername extension.\n",tlsextcbp.ack?"":"not ");
					}
				if (sess_out)
					{
					BIO *stmp = BIO_new_file(sess_out, "w");
					if (stmp)
						{
						PEM_write_bio_SSL_SESSION(stmp, SSL_get_session(con));
						BIO_free(stmp);
						}
					else 
						BIO_printf(bio_err, "Error writing session file %s\n", sess_out);
					}
				print_stuff(bio_c_out,con,full_log);
				if (full_log > 0) full_log--;

					{
					BIO_printf(bio_err,"%s",mbuf);
					/* We don't need to know any more */
					starttls_proto = PROTO_OFF;
				if (reconnect)
					{
					reconnect--;
					BIO_printf(bio_c_out,"drop connection and then reconnect\n");
					SSL_shutdown(con);
					SSL_set_connect_state(con);
					SHUTDOWN(SSL_get_fd(con));
					goto re_start;
					}
				}
			}

		ssl_pending = read_ssl && SSL_pending(con);

		if (!ssl_pending)
Ulf Möller's avatar
Ulf Möller committed
#if !defined(OPENSSL_SYS_WINDOWS) && !defined(OPENSSL_SYS_MSDOS) && !defined(OPENSSL_SYS_NETWARE) && !defined (OPENSSL_SYS_BEOS_R5)
				if (read_tty)  openssl_fdset(fileno(stdin),&readfds);
				if (write_tty) openssl_fdset(fileno(stdout),&writefds);
				openssl_fdset(SSL_get_fd(con),&readfds);
				openssl_fdset(SSL_get_fd(con),&writefds);
					openssl_fdset(SSL_get_fd(con),&readfds);
					openssl_fdset(SSL_get_fd(con),&writefds);
/*			printf("mode tty(%d %d%d) ssl(%d%d)\n",
				tty_on,read_tty,write_tty,read_ssl,write_ssl);*/
Ulf Möller's avatar
Ulf Möller committed
			/* Note: under VMS with SOCKETSHR the second parameter
Ulf Möller's avatar
Ulf Möller committed
			 * is currently of type (int *) whereas under other
			 * systems it is (void *) if you don't have a cast it
			 * will choke the compiler: if you do have a cast then
			 * you can either go for (int *) or (void *).
			 */
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS)
                        /* Under Windows/DOS we make the assumption that we can
			 * always write to the tty: therefore if we need to
			 * write to the tty we just fall through. Otherwise
			 * we timeout the select every second and see if there
			 * are any keypresses. Note: this is a hack, in a proper
			 * Windows application we wouldn't do this.
			 */
Ulf Möller's avatar
Ulf Möller committed
			i=0;
			if(!write_tty) {
				if(read_tty) {
					tv.tv_sec = 1;
					tv.tv_usec = 0;
					i=select(width,(void *)&readfds,(void *)&writefds,
						 NULL,&tv);
#if defined(OPENSSL_SYS_WINCE) || defined(OPENSSL_SYS_MSDOS)
Richard Levitte's avatar
Richard Levitte committed
					if(!i && (!_kbhit() || !read_tty) ) continue;
#else
					if(!i && (!((_kbhit()) || (WAIT_OBJECT_0 == WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), 0))) || !read_tty) ) continue;
Richard Levitte's avatar
Richard Levitte committed
#endif
				} else 	i=select(width,(void *)&readfds,(void *)&writefds,
					 NULL,NULL);
			}
#elif defined(OPENSSL_SYS_NETWARE)
			if(!write_tty) {
				if(read_tty) {
					tv.tv_sec = 1;
					tv.tv_usec = 0;
					i=select(width,(void *)&readfds,(void *)&writefds,
						NULL,&tv);
				} else 	i=select(width,(void *)&readfds,(void *)&writefds,
					NULL,NULL);
			}
Ulf Möller's avatar
Ulf Möller committed
#elif defined(OPENSSL_SYS_BEOS_R5)
			/* Under BeOS-R5 the situation is similar to DOS */
			i=0;
			stdin_set = 0;
			(void)fcntl(fileno(stdin), F_SETFL, O_NONBLOCK);
			if(!write_tty) {
				if(read_tty) {
					tv.tv_sec = 1;
					tv.tv_usec = 0;
					i=select(width,(void *)&readfds,(void *)&writefds,
						 NULL,&tv);
					if (read(fileno(stdin), sbuf, 0) >= 0)
						stdin_set = 1;
					if (!i && (stdin_set != 1 || !read_tty))
						continue;
				} else 	i=select(width,(void *)&readfds,(void *)&writefds,
					 NULL,NULL);
			}
			(void)fcntl(fileno(stdin), F_SETFL, 0);
Ulf Möller's avatar
Ulf Möller committed
			i=select(width,(void *)&readfds,(void *)&writefds,
				 NULL,NULL);
			if ( i < 0)
				{
				BIO_printf(bio_err,"bad select %d\n",
				get_last_socket_error());
		if (!ssl_pending && FD_ISSET(SSL_get_fd(con),&writefds))
			{
			k=SSL_write(con,&(cbuf[cbuf_off]),
				(unsigned int)cbuf_len);
			switch (SSL_get_error(con,k))
				{
			case SSL_ERROR_NONE:
				cbuf_off+=k;
				cbuf_len-=k;
				if (k <= 0) goto end;
				/* we have done a  write(con,NULL,0); */
				if (cbuf_len <= 0)
					{
					read_tty=1;
					write_ssl=0;
					}
				else /* if (cbuf_len > 0) */
					{
					read_tty=0;
					write_ssl=1;
					}
				break;
			case SSL_ERROR_WANT_WRITE:
				BIO_printf(bio_c_out,"write W BLOCK\n");
				write_ssl=1;
				read_tty=0;
				break;
			case SSL_ERROR_WANT_READ:
				BIO_printf(bio_c_out,"write R BLOCK\n");
				write_tty=0;
				read_ssl=1;
				write_ssl=0;
				break;
			case SSL_ERROR_WANT_X509_LOOKUP:
				BIO_printf(bio_c_out,"write X BLOCK\n");
				break;
			case SSL_ERROR_ZERO_RETURN:
				if (cbuf_len != 0)
					{
					BIO_printf(bio_c_out,"shutdown\n");
					goto shut;
					}
				else
					{
					read_tty=1;
					write_ssl=0;
					break;
					}
				
			case SSL_ERROR_SYSCALL:
				if ((k != 0) || (cbuf_len != 0))
					{
					BIO_printf(bio_err,"write:errno=%d\n",
						get_last_socket_error());
					goto shut;
					}
				else
					{
					read_tty=1;
					write_ssl=0;
					}
				break;
			case SSL_ERROR_SSL:
				ERR_print_errors(bio_err);
				goto shut;
				}
			}
Ulf Möller's avatar
Ulf Möller committed
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS) || defined(OPENSSL_SYS_NETWARE) || defined(OPENSSL_SYS_BEOS_R5)
		/* Assume Windows/DOS/BeOS can always write */
		else if (!ssl_pending && FD_ISSET(fileno(stdout),&writefds))
#ifdef CHARSET_EBCDIC
			ascii2ebcdic(&(sbuf[sbuf_off]),&(sbuf[sbuf_off]),sbuf_len);
#endif
			i=raw_write_stdout(&(sbuf[sbuf_off]),sbuf_len);

			if (i <= 0)
				{
				BIO_printf(bio_c_out,"DONE\n");
				goto shut;
				/* goto end; */
				}

			sbuf_len-=i;;
			sbuf_off+=i;
			if (sbuf_len <= 0)
				{
				read_ssl=1;
				write_tty=0;
				}
			}
		else if (ssl_pending || FD_ISSET(SSL_get_fd(con),&readfds))
#ifdef RENEG
{ static int iiii; if (++iiii == 52) { SSL_renegotiate(con); iiii=0; } }
#endif
			k=SSL_read(con,sbuf,1024 /* BUFSIZZ */ );
#else
/* Demo for pending and peek :-) */
			k=SSL_read(con,sbuf,16);
{ char zbuf[10240]; 
printf("read=%d pending=%d peek=%d\n",k,SSL_pending(con),SSL_peek(con,zbuf,10240));
}
#endif

			switch (SSL_get_error(con,k))
				{
			case SSL_ERROR_NONE:
				if (k <= 0)
					goto end;
				sbuf_off=0;
				sbuf_len=k;

				read_ssl=0;
				write_tty=1;
				break;
			case SSL_ERROR_WANT_WRITE:
				BIO_printf(bio_c_out,"read W BLOCK\n");
				write_ssl=1;
				read_tty=0;
				break;
			case SSL_ERROR_WANT_READ:
				BIO_printf(bio_c_out,"read R BLOCK\n");
				write_tty=0;
				read_ssl=1;
				if ((read_tty == 0) && (write_ssl == 0))
					write_ssl=1;
				break;
			case SSL_ERROR_WANT_X509_LOOKUP:
				BIO_printf(bio_c_out,"read X BLOCK\n");
				break;
			case SSL_ERROR_SYSCALL:
				ret=get_last_socket_error();
				BIO_printf(bio_err,"read:errno=%d\n",ret);
				goto shut;
			case SSL_ERROR_ZERO_RETURN:
				BIO_printf(bio_c_out,"closed\n");
				goto shut;
			case SSL_ERROR_SSL:
				ERR_print_errors(bio_err);
				goto shut;
#if defined(OPENSSL_SYS_WINDOWS) || defined(OPENSSL_SYS_MSDOS)
#if defined(OPENSSL_SYS_WINCE) || defined(OPENSSL_SYS_MSDOS)
Richard Levitte's avatar
Richard Levitte committed
		else if (_kbhit())
#else
		else if ((_kbhit()) || (WAIT_OBJECT_0 == WaitForSingleObject(GetStdHandle(STD_INPUT_HANDLE), 0)))
Richard Levitte's avatar
Richard Levitte committed
#endif
#elif defined (OPENSSL_SYS_NETWARE)
Ulf Möller's avatar
Ulf Möller committed
#elif defined(OPENSSL_SYS_BEOS_R5)
		else if (stdin_set)
		else if (FD_ISSET(fileno(stdin),&readfds))
				i=raw_read_stdin(cbuf,BUFSIZZ/2);
				lf_num = 0;
				/* both loops are skipped when i <= 0 */
				for (j = 0; j < i; j++)
					if (cbuf[j] == '\n')
						lf_num++;
				for (j = i-1; j >= 0; j--)
					{
					cbuf[j+lf_num] = cbuf[j];
					if (cbuf[j] == '\n')
						{
						lf_num--;
						i++;
						cbuf[j+lf_num] = '\r';
						}
					}
				assert(lf_num == 0);
				}
			else
				i=raw_read_stdin(cbuf,BUFSIZZ);
			if ((!c_ign_eof) && ((i <= 0) || (cbuf[0] == 'Q')))
				{
				BIO_printf(bio_err,"DONE\n");
			if ((!c_ign_eof) && (cbuf[0] == 'R'))
				BIO_printf(bio_err,"RENEGOTIATING\n");
				SSL_renegotiate(con);
				cbuf_len=0;
#ifdef CHARSET_EBCDIC
				ebcdic2ascii(cbuf, cbuf, i);
#endif
			read_tty=0;
	if (in_init)
		print_stuff(bio_c_out,con,full_log);
	SSL_shutdown(con);
	SHUTDOWN(SSL_get_fd(con));
end:
	if (con != NULL)
		{
		if (prexit != 0)
			print_stuff(bio_c_out,con,1);
		SSL_free(con);
		}
	if (ctx != NULL) SSL_CTX_free(ctx);
Dr. Stephen Henson's avatar
Dr. Stephen Henson committed
	if (cert)
		X509_free(cert);
	if (key)
		EVP_PKEY_free(key);
	if (pass)
		OPENSSL_free(pass);
	if (cbuf != NULL) { OPENSSL_cleanse(cbuf,BUFSIZZ); OPENSSL_free(cbuf); }
	if (sbuf != NULL) { OPENSSL_cleanse(sbuf,BUFSIZZ); OPENSSL_free(sbuf); }
	if (mbuf != NULL) { OPENSSL_cleanse(mbuf,BUFSIZZ); OPENSSL_free(mbuf); }
	if (bio_c_out != NULL)
		{
		BIO_free(bio_c_out);
		bio_c_out=NULL;
		}
Ulf Möller's avatar
Ulf Möller committed
static void print_stuff(BIO *bio, SSL *s, int full)
Nils Larsch's avatar
Nils Larsch committed
	static const char *space="                ";
	STACK_OF(X509) *sk;
	STACK_OF(X509_NAME) *sk2;
	const SSL_CIPHER *c;
#ifndef OPENSSL_NO_COMP
	const COMP_METHOD *comp, *expansion;
		sk=SSL_get_peer_cert_chain(s);
		if (sk != NULL)
			{
			got_a_chain = 1; /* we don't have it for SSL2 (yet) */

			BIO_printf(bio,"---\nCertificate chain\n");
			for (i=0; i<sk_X509_num(sk); i++)
				X509_NAME_oneline(X509_get_subject_name(
					sk_X509_value(sk,i)),buf,sizeof buf);
				BIO_printf(bio,"%2d s:%s\n",i,buf);
				X509_NAME_oneline(X509_get_issuer_name(
					sk_X509_value(sk,i)),buf,sizeof buf);
				BIO_printf(bio,"   i:%s\n",buf);
				if (c_showcerts)
					PEM_write_bio_X509(bio,sk_X509_value(sk,i));
				}
			}

		BIO_printf(bio,"---\n");
		peer=SSL_get_peer_certificate(s);
		if (peer != NULL)
			{
			BIO_printf(bio,"Server certificate\n");
			if (!(c_showcerts && got_a_chain)) /* Redundant if we showed the whole chain */
				PEM_write_bio_X509(bio,peer);
			X509_NAME_oneline(X509_get_subject_name(peer),
				buf,sizeof buf);
			BIO_printf(bio,"subject=%s\n",buf);
			X509_NAME_oneline(X509_get_issuer_name(peer),
				buf,sizeof buf);
			BIO_printf(bio,"issuer=%s\n",buf);
			}
		else
			BIO_printf(bio,"no peer certificate available\n");

		sk2=SSL_get_client_CA_list(s);
		if ((sk2 != NULL) && (sk_X509_NAME_num(sk2) > 0))
			{
			BIO_printf(bio,"---\nAcceptable client certificate CA names\n");
			for (i=0; i<sk_X509_NAME_num(sk2); i++)
				xn=sk_X509_NAME_value(sk2,i);
				X509_NAME_oneline(xn,buf,sizeof(buf));
				BIO_write(bio,buf,strlen(buf));
				BIO_write(bio,"\n",1);
				}
			}
		else
			{
			BIO_printf(bio,"---\nNo client certificate CA names sent\n");
			}
		p=SSL_get_shared_ciphers(s,buf,sizeof buf);
Bodo Möller's avatar
Bodo Möller committed
			/* This works only for SSL 2.  In later protocol
			 * versions, the client does not know what other
			 * ciphers (in addition to the one to be used
			 * in the current connection) the server supports. */

			BIO_printf(bio,"---\nCiphers common between both SSL endpoints:\n");
			j=i=0;
			while (*p)
				{
				if (*p == ':')
					{
					BIO_write(bio,space,15-j%25);
					i++;
					j=0;
					BIO_write(bio,((i%3)?" ":"\n"),1);
					}
				else
					{
					BIO_write(bio,p,1);
					j++;
					}
				p++;
				}
			BIO_write(bio,"\n",1);
			}

		BIO_printf(bio,"---\nSSL handshake has read %ld bytes and written %ld bytes\n",
			BIO_number_read(SSL_get_rbio(s)),
			BIO_number_written(SSL_get_wbio(s)));
		}
	BIO_printf(bio,((s->hit)?"---\nReused, ":"---\nNew, "));
	c=SSL_get_current_cipher(s);
	BIO_printf(bio,"%s, Cipher is %s\n",
		SSL_CIPHER_get_version(c),
		SSL_CIPHER_get_name(c));
	if (peer != NULL) {
		EVP_PKEY *pktmp;
		pktmp = X509_get_pubkey(peer);
		BIO_printf(bio,"Server public key is %d bit\n",
							 EVP_PKEY_bits(pktmp));
		EVP_PKEY_free(pktmp);
	}
#ifndef OPENSSL_NO_COMP
	expansion=SSL_get_current_expansion(s);
	BIO_printf(bio,"Compression: %s\n",
		comp ? SSL_COMP_get_name(comp) : "NONE");
	BIO_printf(bio,"Expansion: %s\n",
		expansion ? SSL_COMP_get_name(expansion) : "NONE");
	SSL_SESSION_print(bio,SSL_get_session(s));
	BIO_printf(bio,"---\n");
	if (peer != NULL)
		X509_free(peer);
	/* flush, or debugging output gets mixed with http response */
Dr. Stephen Henson's avatar
Dr. Stephen Henson committed
	(void)BIO_flush(bio);
#ifndef OPENSSL_NO_TLSEXT

static int ocsp_resp_cb(SSL *s, void *arg)
	{
	const unsigned char *p;
	int len;
	OCSP_RESPONSE *rsp;
	len = SSL_get_tlsext_status_ocsp_resp(s, &p);
	BIO_puts(arg, "OCSP response: ");
	if (!p)
		{
		BIO_puts(arg, "no response sent\n");
		return 1;
		}
	rsp = d2i_OCSP_RESPONSE(NULL, &p, len);
	if (!rsp)
		{
		BIO_puts(arg, "response parse error\n");
		BIO_dump_indent(arg, (char *)p, len, 4);
		return 0;
		}
	BIO_puts(arg, "\n======================================\n");
	OCSP_RESPONSE_print(arg, rsp, 0);
	BIO_puts(arg, "======================================\n");
	OCSP_RESPONSE_free(rsp);
	return 1;
	}