35
36:- module(http_ssl_plugin, []). 38:- use_module(library(ssl),
39 [ ssl_context/3,
40 ssl_secure_ciphers/1,
41 ssl_property/2,
42 ssl_set_options/3,
43 ssl_negotiate/5
44 ]). 45:- use_module(library(debug),[debug/3]). 46:- use_module(library(socket),
47 [ tcp_socket/1,
48 tcp_setopt/2,
49 tcp_bind/2,
50 tcp_listen/2,
51 tcp_accept/3,
52 tcp_open_socket/3,
53 tcp_connect/3
54 ]). 55
56:- autoload(library(lists),[select/3]). 57:- autoload(library(option),[option/2,option/3]). 58:- autoload(library(apply), [include/3]). 59:- autoload(library(http/http_header),[http_read_reply_header/2]). 60:- autoload(library(http/thread_httpd),[http_enough_workers/3]).
73:- multifile
74 thread_httpd:make_socket_hook/3,
75 thread_httpd:accept_hook/2,
76 thread_httpd:open_client_hook/6,
77 http:http_protocol_hook/5,
78 http:open_options/2,
79 http:http_connection_over_proxy/6,
80 http:ssl_server_create_hook/3,
81 http:ssl_server_open_client_hook/3. 82
83
84
96thread_httpd:make_socket_hook(Port, M:Options0, Options) :-
97 select(ssl(SSLOptions0), Options0, Options1),
98 !,
99 add_secure_ciphers(SSLOptions0, SSLOptions1),
100 disable_sslv3(SSLOptions1, SSLOptions),
101 make_socket(Port, Socket, Options1),
102 ssl_context(server, SSL0, M:[close_parent(true)|SSLOptions]),
103 ( http:ssl_server_create_hook(SSL0, SSL1, Options1)
104 -> ensure_close_parent(SSL1, SSL)
105 ; SSL = SSL0
106 ),
107 atom_concat('httpsd', Port, Queue),
108 Options = [ queue(Queue),
109 tcp_socket(Socket),
110 ssl_instance(SSL)
111 | Options1
112 ].
113
114ensure_close_parent(SSL0, SSL) :-
115 ( ssl_property(SSL0, close_parent(true))
116 -> SSL = SSL0
117 ; ssl_set_options(SSL0, SSL, [close_parent(true)])
118 ).
124add_secure_ciphers(SSLOptions0, SSLOptions) :-
125 ( option(cipher_list(_), SSLOptions0)
126 -> SSLOptions = SSLOptions0
127 ; ssl_secure_ciphers(Ciphers),
128 SSLOptions = [cipher_list(Ciphers)|SSLOptions0]
129 ).
137disable_sslv3(SSLOptions0, SSLOptions) :-
138 ( option(min_protocol_version(_), SSLOptions0)
139 ; option(disable_ssl_methods(_), SSLOptions0)
140 ),
141 !,
142 SSLOptions = SSLOptions0.
143disable_sslv3(SSLOptions0,
144 [ disable_ssl_methods([sslv3,sslv23]), 145 min_protocol_version(tlsv1) 146 | SSLOptions0
147 ]).
148
149
150make_socket(_Port, Socket, Options) :-
151 option(tcp_socket(Socket), Options),
152 !.
153make_socket(Port, Socket, _Options) :-
154 tcp_socket(Socket),
155 tcp_setopt(Socket, reuseaddr),
156 tcp_bind(Socket, Port),
157 tcp_listen(Socket, 5).
164thread_httpd:accept_hook(Goal, Options) :-
165 memberchk(ssl_instance(SSL0), Options),
166 !,
167 memberchk(queue(Queue), Options),
168 memberchk(tcp_socket(Socket), Options),
169 tcp_accept(Socket, Client, Peer),
170 debug(http(connection), 'New HTTPS connection from ~p', [Peer]),
171 http_enough_workers(Queue, accept, Peer),
172 ensure_close_parent(SSL0, SSL),
173 thread_send_message(Queue, ssl_client(SSL, Client, Goal, Peer)).
196thread_httpd:open_client_hook(ssl_client(SSL0, Client, Goal, Peer),
197 Goal, In, Out,
198 [peer(Peer), protocol(https)],
199 Options) :-
200 ( http:ssl_server_open_client_hook(SSL0, SSL, Options)
201 -> true
202 ; SSL = SSL0
203 ),
204 option(timeout(TMO), Options, 60),
205 tcp_open_socket(Client, Read, Write),
206 set_stream(Read, timeout(TMO)),
207 set_stream(Write, timeout(TMO)),
208 catch(ssl_negotiate(SSL, Read, Write, In, Out),
209 E,
210 ssl_failed(Read, Write, E)).
211
212ssl_failed(Read, Write, E) :-
213 close(Write, [force(true)]),
214 close(Read, [force(true)]),
215 throw(E).
216
217
218
228http:http_protocol_hook(https, Parts, PlainStreamPair, StreamPair, Options) :-
229 ssl_protocol_hook(Parts, PlainStreamPair, StreamPair, Options).
230http:http_protocol_hook(wss, Parts, PlainStreamPair, StreamPair, Options) :-
231 ssl_protocol_hook(Parts, PlainStreamPair, StreamPair, Options).
232
233ssl_protocol_hook(Parts, PlainStreamPair, StreamPair, Options) :-
234 memberchk(host(Host), Parts),
235 include(ssl_option, Options, SSLOptions),
236 ssl_context(client, SSL, [ host(Host),
237 close_parent(true)
238 | SSLOptions
239 ]),
240 stream_pair(PlainStreamPair, PlainIn, PlainOut),
241 242 ssl_negotiate(SSL, PlainIn, PlainOut, In, Out),
243 stream_pair(StreamPair, In, Out).
244
247
248ssl_option(Term) :-
249 compound(Term),
250 compound_name_arity(Term, _, 1).
259http:http_connection_over_proxy(proxy(ProxyHost, ProxyPort), Parts,
260 Host:Port, StreamPair, Options, Options) :-
261 memberchk(scheme(https), Parts),
262 !,
263 tcp_connect(ProxyHost:ProxyPort, StreamPair, [bypass_proxy(true)]),
264 catch(negotiate_http_connect(StreamPair, Host:Port),
265 Error,
266 ( close(StreamPair, [force(true)]),
267 throw(Error)
268 )).
269
270negotiate_http_connect(StreamPair, Address):-
271 format(StreamPair, 'CONNECT ~w HTTP/1.1\r\n\r\n', [Address]),
272 flush_output(StreamPair),
273 http_read_reply_header(StreamPair, Header),
274 memberchk(status(_, Status, Message), Header),
275 ( Status == ok
276 -> true
277 ; throw(error(proxy_rejection(Message), _))
278 )
SSL plugin for HTTP libraries
This module can be loaded next to
library(thread_httpd)
andlibrary(http_open)
to provide secure HTTP (HTTPS) services and client access.An example secure server using self-signed certificates can be found in the <plbase>/
doc/packages/examples/ssl/https.pl
, where <plbase> is the SWI-Prolog installation directory. */