@@ -748,10 +748,11 @@ char *CAdir = NULL; char *DHparams = NULL; char *model = NULL; #ifndef OPENSSL_NO_TLSEXT char *servername = NULL; /* hostname for Server Name Indication */ + Tcl_Obj *alpn = NULL; #endif int ssl2 = 0, ssl3 = 0; int tls1 = 1, tls1_1 = 1, tls1_2 = 1, tls1_3 = 1; int proto = 0; int verify = 0, require = 0, request = 1; @@ -810,10 +811,11 @@ OPTBOOL( "-require", require); OPTBOOL( "-request", request); OPTBOOL( "-server", server); #ifndef OPENSSL_NO_TLSEXT OPTSTR( "-servername", servername); + OPTOBJ( "-alpn", alpn); #endif OPTBOOL( "-ssl2", ssl2); OPTBOOL( "-ssl3", ssl3); OPTBOOL( "-tls1", tls1); @@ -821,11 +823,11 @@ OPTBOOL( "-tls1.2", tls1_2); OPTBOOL( "-tls1.3", tls1_3); OPTBYTE("-cert", cert, cert_len); OPTBYTE("-key", key, key_len); - OPTBAD( "option", "-cadir, -cafile, -cert, -certfile, -cipher, -command, -dhparams, -key, -keyfile, -model, -password, -require, -request, -server, -servername, -ssl2, -ssl3, -tls1, -tls1.1, -tls1.2, or tls1.3"); + OPTBAD( "option", "-alpn, -cadir, -cafile, -cert, -certfile, -cipher, -command, -dhparams, -key, -keyfile, -model, -password, -require, -request, -server, -servername, -ssl2, -ssl3, -tls1, -tls1.1, -tls1.2, or tls1.3"); return TCL_ERROR; } if (request) verify |= SSL_VERIFY_CLIENT_ONCE | SSL_VERIFY_PEER; if (request && require) verify |= SSL_VERIFY_FAIL_IF_NO_PEER_CERT; @@ -958,10 +960,51 @@ (char *) NULL); Tls_Free((char *) statePtr); return TCL_ERROR; } } + if (alpn) { + /* Convert a Tcl list into a protocol-list in wire-format */ + unsigned char *protos, *p; + unsigned int protoslen = 0; + int i, len, cnt; + Tcl_Obj **list; + if (Tcl_ListObjGetElements(interp, alpn, &cnt, &list) != TCL_OK) { + Tls_Free((char *) statePtr); + return TCL_ERROR; + } + /* Determine the memory required for the protocol-list */ + for (i = 0; i < cnt; i++) { + Tcl_GetStringFromObj(list[i], &len); + if (len > 255) { + Tcl_AppendResult(interp, "alpn protocol name too long", + (char *) NULL); + Tls_Free((char *) statePtr); + return TCL_ERROR; + } + protoslen += 1 + len; + } + /* Build the complete protocol-list */ + protos = ckalloc(protoslen); + /* protocol-lists consist of 8-bit length-prefixed, byte strings */ + for (i = 0, p = protos; i < cnt; i++) { + char *str = Tcl_GetStringFromObj(list[i], &len); + *p++ = len; + memcpy(p, str, len); + p += len; + } + /* Note: This functions reverses the return value convention */ + if (SSL_set_alpn_protos(statePtr->ssl, protos, protoslen)) { + Tcl_AppendResult(interp, "failed to set alpn protocols", + (char *) NULL); + Tls_Free((char *) statePtr); + ckfree(protos); + return TCL_ERROR; + } + /* SSL_set_alpn_protos makes a copy of the protocol-list */ + ckfree(protos); + } #endif /* * SSL Callbacks */ @@ -1409,10 +1452,14 @@ X509 *peer; Tcl_Obj *objPtr; Tcl_Channel chan; char *channelName, *ciphers; int mode; +#ifndef OPENSSL_NO_TLSEXT + const unsigned char *proto; + unsigned int len; +#endif dprintf("Called"); switch (objc) { case 2: @@ -1423,10 +1470,11 @@ if (!strcmp (Tcl_GetString (objv[1]), "-local")) { channelName = Tcl_GetStringFromObj(objv[2], NULL); break; } /* else fall... */ + __attribute__((fallthrough)); default: Tcl_WrongNumArgs(interp, 1, objv, "?-local? channel"); return TCL_ERROR; } @@ -1466,10 +1514,18 @@ Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("cipher", -1)); Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj(SSL_get_cipher(statePtr->ssl), -1)); } + +#ifndef OPENSSL_NO_TLSEXT + /* Report the selected protocol as a result of the negotiation */ + SSL_get0_alpn_selected(statePtr->ssl, &proto, &len); + Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("alpn", -1)); + Tcl_ListObjAppendElement(interp, objPtr, + Tcl_NewStringObj((char *)proto, (int)len)); +#endif Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj("version", -1)); Tcl_ListObjAppendElement(interp, objPtr, Tcl_NewStringObj(SSL_get_version(statePtr->ssl), -1));