Index: doc/tls.html ================================================================== --- doc/tls.html +++ doc/tls.html @@ -19,11 +19,11 @@ for socket and I/O channel communications.
SYNOPSIS
-
package require Tcl ?8.5?
+
package require Tcl ?8.5?
package require tls
 
tls::init ?options?
tls::socket ?options? host port
tls::socket ?-server command? ?options? port
@@ -51,11 +51,11 @@

tls - binding to OpenSSL library for socket and I/O channel communications.

SYNOPSIS

-

package require Tcl 8.5
+

package require Tcl ?8.5?
package require tls

tls::init ?options?
tls::socket ?options? host port
tls::socket ?-server command? ?options? port
@@ -69,16 +69,17 @@ tls::version

DESCRIPTION

-

This extension provides a generic binding to OpenSSL, utilizing the -Tcl_StackChannel -API for Tcl 8.4 and higher. The sockets behave exactly the same -as channels created using Tcl's built-in socket -command with additional options for controlling the SSL session. +

This extension provides TCL script access to secure socket communications +using the Transport Layer Security (TLS) protocol. It provides a generic +binding to OpenSSL, utilizing the +Tcl_StackChannel API in Tcl 8.4 and higher. +These sockets behave exactly the same as channels created using the built-in +socket command, along with additional options for controlling +the SSL session.

COMMANDS

Typically one would use the tls::socket command @@ -459,26 +460,29 @@

- error channel message + error channelId message
- The message argument contains an error message generated - by the OpenSSL function ERR_reason_error_string(). + This form of callback is invoked whenever an error occurs during the + initial connection, handshake, or I/O operations. The message + argument can be from the Tcl_ErrnoMsg, OpenSSL function + ERR_reason_error_string(), or a custom message.

- info channel major minor message type + info channelId major minor message type
This form of callback is invoked by the OpenSSL function - SSL_CTX_set_info_callback() during connection setup - and use. + SSL_set_info_callback() during the initial connection + and handshake operations. The type argument is new for + TLS 1.8. The arguments are:
  • Possible values for major are: handshake, alert, connect, accept.
  • Possible values for minor are: @@ -491,32 +495,37 @@ info is used.
- message channel direction version content_type data + message channelId direction version content_type message
This form of callback is invoked by the OpenSSL function SSL_set_msg_callback() whenever a message is sent or - received. It is only available when - OpenSSL is complied with the enable-ssl-trace option. - Where direction is Sent or Received, version is the - protocol version, content_type is the message content type, - and data is more info on the message from the SSL_trace API. + received during the initial connection, handshake, or I/O operations. + It is only available when OpenSSL is complied with the + enable-ssl-trace option. Arguments are: direction + is Sent or Received, version is the protocol + version, content_type is the message content type, and + message is more info from the SSL_trace API. + This callback is new for TLS 1.8.

- session channel session_id ticket lifetime + session channelId session_id ticket lifetime
This form of callback is invoked by the OpenSSL function - SSL_CTX_sess_set_new_cb(). - Where session_id is the current session identifier, - ticket is the session ticket info, and lifetime - is the the ticket lifetime in seconds. + SSL_CTX_sess_set_new_cb() whenever a new session id is + sent by the server during the initial connection and handshake, but + can also be received later if the -post_handshake option is + used. Arguments are: session_id is the current + session identifier, ticket is the session ticket info, and + lifetime is the the ticket lifetime in seconds. + This callback is new for TLS 1.8.

@@ -540,10 +549,11 @@ Invoked when loading or storing a PEM certificate with encryption. Where rwflag is 0 for reading/decryption or 1 for writing/encryption (can prompt user to confirm) and size is the max password length in bytes. The callback should return the password as a string. + Both arguments are new for TLS 1.8.
@@ -550,79 +560,82 @@
-validatecommand callback
Invokes the specified callback script during handshake in order to validate the provided value(s). See below for the possible - arguments passed to the callback script. - To reject the value and abort connection, the callback should return 0. + arguments passed to the callback script. If not specified, OpenSSL + will accept valid certificates and extensions. + To reject the value and abort the connection, the callback should return 0. To accept the value and continue the connection, it should return 1. To reject the value, but continue the connection, it should return 2.

- alpn channel protocol match -
-
- For servers, this form of callback is invoked when the client ALPN - extension is received. If match is true, protocol - is the first -alpn specified protocol common to the both the - client and server. If not, the first client specified protocol is - used. Called after hello and ALPN callbacks. -
- -
- -
- hello channel servername -
-
- For servers, this form of callback is invoked during client hello - message processing. It is used to select an appropriate certificate to - present, and make other configuration adjustments relevant to that - server name and its configuration. Called before SNI and ALPN callbacks. -
- -
- -
- sni channel servername -
-
- For servers, this form of callback is invoked when the SNI extension - from the client is received. Where servername is the client - provided server name from the -servername option. This is - used when a server supports multiple names, so the right certificate - can be used. Called after hello callback but before ALPN callback. -
- -
- -
- verify channel depth cert status error + alpn channelId protocol match +
+
+ For servers, this form of callback is invoked when the client ALPN + extension is received. If match is true, protocol + is the first -alpn option specified protocol common to both + the client and server. If not, the first client specified protocol is + used. It is called after the hello and ALPN callbacks. + This callback is new for TLS 1.8. +
+ +
+ +
+ hello channelId servername +
+
+ For servers, this form of callback is invoked during client hello + message processing. The purpose is so the server can select the + appropriate certificate to present to the client, and to make other + configuration adjustments relevant to that server name and its + configuration. It is called before the SNI and ALPN callbacks. + This callback is new for TLS 1.8. +
+ +
+ +
+ sni channelId servername +
+
+ For servers, this form of callback is invoked when the Server Name + Indication (SNI) extension is received. The servername + argument is the client provided server name in the -servername + option. The purpose is so when a server supports multiple names, the + right certificate can be used. It is called after the hello callback + but before the ALPN callback. + This callback is new for TLS 1.8. +
+ +
+ +
+ verify channelId depth cert status error
This form of callback is invoked by OpenSSL when a new certificate is received from the peer. It allows the client to check the certificate verification results and choose whether to continue or not. It is called for each certificate in the certificate chain.
    -
  • The depth argument is an integer representing the - current depth on the certificate chain, with - 0 as the peer certificate and higher values going - up to the Certificate Authority (CA).
  • +
  • The depth argument is the integer depth of the + certificate in the certificate chain, where 0 is the peer certificate + and higher values going up to the Certificate Authority (CA).
  • The cert argument is a list of key-value pairs similar to those returned by tls::status.
  • -
  • The status argument is an boolean representing the - validity of the current certificate. - A value of 0 means the certificate is deemed invalid. - A value of 1 means the certificate is deemed valid.
  • -
  • The error argument supplies the message, if any, generated +
  • The status argument is the boolean validity of the + current certificate where 0 is invalid and 1 is valid.
  • +
  • The error argument is the error message, if any, generated by X509_STORE_CTX_get_error().

@@ -678,32 +691,48 @@ The use of the variable tls::debug is not recommended. It may be removed from future releases.

+
+Example #1: Use HTTP package -Example: +

This example uses the default Unix platform SSL certificates. For standard +installations, -cadir and -cafile should not be needed. Update -cadir or +replace with -cafile if your platform differs.

+

 package require http
 package require tls
+set url "https://www.tcl.tk/"
 
 http::register https 443 [list ::tls::socket -autoservername true -require true -cadir /etc/ssl/certs \
     -command ::tls::callback -password ::tls::password -validatecommand ::tls::validate_command]
 
-set token [http::geturl "https://www.tcl-lang.org/"]
+# Check for error
+set token [http::geturl $url]
+if {[http::status $token] ne "ok"} {
+    puts [format "Error %s" [http::status $token]]
+}
+
+# Get web page
+set data [http::data $token]
+puts [string length $data]
+
+# Cleanup
 ::http::cleanup $token
 
-Example #2: +Example #2: Use raw socket

 package require tls
 
 set url "www.tcl-lang.org"
 set port 443
 
 set ch [tls::socket -autoservername 1 -servername $url -request 1 -require 1 \
-    -alpn {http/1.1 h2} -cadir /etc/ssl/certs -command ::tls::callback \
+    -alpn {http/1.1} -cadir /etc/ssl/certs -command ::tls::callback \
     -password ::tls::password -validatecommand ::tls::validate_command $url $port]
 chan configure $ch -buffersize 65536
 tls::handshake $ch
 
 puts $ch "GET / HTTP/1.1"
@@ -721,20 +750,32 @@
 

HTTPS EXAMPLE

-

This example uses a sample server.pem provided with the TLS release, -courtesy of the OpenSSL project.

+

This example uses the default Unix platform SSL certificates. For standard +installations, -cadir and -cafile should not be needed. Update -cadir or +replace with -cafile if your platform differs.


 package require http
 package require tls
+set url "https://www.tcl.tk/"
 
 http::register https 443 [list ::tls::socket -autoservername true -require true -cadir /etc/ssl/certs]
 
-set token [http::geturl https://www.tcl.tk/]
+# Check for error
+set token [http::geturl $url]
+if {[http::status $token] ne "ok"} {
+    puts [format "Error %s" [http::status $token]]
+}
+
+# Get web page
+set data [http::data $token]
+puts [string length $data]
+
+# Cleanup
 ::http::cleanup $token
 

SPECIAL CONSIDERATIONS

Index: generic/tls.c ================================================================== --- generic/tls.c +++ generic/tls.c @@ -186,11 +186,11 @@ else if (where & SSL_CB_LOOP) minor = "loop"; else if (where & SSL_CB_EXIT) minor = "exit"; else minor = "unknown"; } - /* Create command to eval */ + /* Create command to eval with fn, chan, major, minor, message, and type args */ cmdPtr = Tcl_DuplicateObj(statePtr->callback); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("info", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(major, -1)); @@ -312,11 +312,11 @@ buffer[n] = 0; (void)BIO_flush(bio); BIO_free(bio); } - /* Create command to eval */ + /* Create command to eval with fn, chan, direction, version, type, and message args */ cmdPtr = Tcl_DuplicateObj(statePtr->callback); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("message", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(write_p ? "Sent" : "Received", -1)); @@ -390,11 +390,11 @@ return 0; } dprintf("VerifyCallback: eval callback"); - /* Create command to eval */ + /* Create command to eval with fn, chan, depth, cert info list, status, and error args */ cmdPtr = Tcl_DuplicateObj(statePtr->vcmd); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("verify", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(depth)); @@ -440,11 +440,11 @@ dprintf("Called"); if (statePtr->callback == (Tcl_Obj*)NULL) return; - /* Create command to eval */ + /* Create command to eval with fn, chan, and message args */ cmdPtr = Tcl_DuplicateObj(statePtr->callback); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("error", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1)); if (msg != NULL) { @@ -530,11 +530,11 @@ } else { return -1; } } - /* Create command to eval */ + /* Create command to eval with fn, rwflag, and size args */ cmdPtr = Tcl_DuplicateObj(statePtr->password); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("password", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(rwflag)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewIntObj(size)); @@ -609,11 +609,11 @@ return SSL_TLSEXT_ERR_OK; } else if (ssl == NULL) { return SSL_TLSEXT_ERR_NOACK; } - /* Create command to eval */ + /* Create command to eval with fn, chan, session id, session ticket, and lifetime args */ cmdPtr = Tcl_DuplicateObj(statePtr->callback); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("session", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1)); @@ -686,11 +686,11 @@ if (statePtr->vcmd == (Tcl_Obj*)NULL) { return res; } - /* Create command to eval */ + /* Create command to eval with fn, chan, depth, cert info list, status, and error args */ cmdPtr = Tcl_DuplicateObj(statePtr->vcmd); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("alpn", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj((const char *) *out, -1)); @@ -799,11 +799,11 @@ if (statePtr->vcmd == (Tcl_Obj*)NULL) { return SSL_TLSEXT_ERR_OK; } - /* Create command to eval */ + /* Create command to eval with fn, chan, and server name args */ cmdPtr = Tcl_DuplicateObj(statePtr->vcmd); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("sni", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(servername , -1)); @@ -901,11 +901,11 @@ return SSL_CLIENT_HELLO_ERROR; } remaining = len; servername = (const char *)p; - /* Create command to eval */ + /* Create command to eval with fn, chan, and server name args */ cmdPtr = Tcl_DuplicateObj(statePtr->vcmd); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj("hello", -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(Tcl_GetChannelName(statePtr->self), -1)); Tcl_ListObjAppendElement(interp, cmdPtr, Tcl_NewStringObj(servername, (Tcl_Size) len));