701 lines
19 KiB
Plaintext
Executable File
701 lines
19 KiB
Plaintext
Executable File
create or replace package body ntlm_http_pkg
|
|
as
|
|
|
|
/*
|
|
|
|
Purpose: Package handles HTTP connections using NTLM authentication
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
FDL 03.06.2011 Created
|
|
MBR 03.06.2011 Troubleshooting, bug fixes, handle persistent connection issues
|
|
MBR 24.06.2011 Cleaned up code
|
|
MBR 24.06.2011 Added begin/end_request
|
|
|
|
*/
|
|
|
|
|
|
function headers_contain_value (p_resp in out utl_http.resp,
|
|
p_name in varchar2,
|
|
p_value in varchar2) return boolean
|
|
as
|
|
l_name varchar2(2000);
|
|
l_value varchar2(2000);
|
|
l_returnvalue boolean := false;
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: return true if response headers contain a specific name/value pair
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 25.07.2011 Created
|
|
|
|
*/
|
|
|
|
for i in 1 .. utl_http.get_header_count (p_resp) loop
|
|
utl_http.get_header (p_resp, i, l_name, l_value);
|
|
if (l_name = p_name) and (l_value = p_value) then
|
|
l_returnvalue := true;
|
|
exit;
|
|
end if;
|
|
end loop;
|
|
|
|
return l_returnvalue;
|
|
|
|
end headers_contain_value;
|
|
|
|
|
|
function get_response_blob (p_url in varchar2,
|
|
p_username in varchar2,
|
|
p_password in varchar2,
|
|
p_wallet_path in varchar2 := null,
|
|
p_wallet_password in varchar2 := null,
|
|
p_proxy_server in varchar2 := null) return blob
|
|
as
|
|
|
|
l_req utl_http.req;
|
|
l_resp utl_http.resp;
|
|
l_returnvalue blob;
|
|
|
|
l_authenticate_with_ntlm boolean;
|
|
|
|
l_name varchar2(500);
|
|
l_value varchar2(500);
|
|
|
|
l_ntlm_message varchar2(500);
|
|
|
|
l_negotiate_message varchar2(500);
|
|
|
|
l_server_challenge raw(4000);
|
|
l_negotiate_flags raw(4000);
|
|
|
|
l_authenticate_message varchar2(500);
|
|
|
|
|
|
function get_response_body (p_resp in out utl_http.resp) return blob
|
|
as
|
|
l_data raw(32767);
|
|
l_returnvalue blob;
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: get the response body as a blob
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 24.06.2011 Created
|
|
|
|
*/
|
|
|
|
dbms_lob.createtemporary( l_returnvalue, true );
|
|
|
|
begin
|
|
loop
|
|
utl_http.read_raw(r => p_resp, data => l_data);
|
|
dbms_lob.append( l_returnvalue, to_blob( l_data ));
|
|
end loop;
|
|
exception
|
|
when utl_http.end_of_body then
|
|
null;
|
|
end;
|
|
|
|
return l_returnvalue;
|
|
|
|
end get_response_body;
|
|
|
|
|
|
procedure debug_response (p_resp in out utl_http.resp)
|
|
as
|
|
l_name varchar2(255);
|
|
l_value varchar2(2000);
|
|
l_body blob;
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: print debug info about the response
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 24.06.2011 Created
|
|
|
|
*/
|
|
|
|
debug_pkg.printf('Response Status Code: %1', p_resp.status_code);
|
|
|
|
for i in 1 .. utl_http.get_header_count (p_resp) loop
|
|
utl_http.get_header (p_resp, i, l_name, l_value);
|
|
debug_pkg.printf('#%1 %2 : %3', i, l_name, l_value);
|
|
end loop;
|
|
|
|
l_returnvalue := get_response_body (p_resp);
|
|
|
|
debug_pkg.printf('Body length = %1', dbms_lob.getlength (l_returnvalue));
|
|
debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count);
|
|
|
|
end debug_response;
|
|
|
|
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: Get response clob from URL
|
|
|
|
Remarks: see http://davenport.sourceforge.net/ntlm.html#ntlmHttpAuthentication
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
FDL 11.05.2011 Created
|
|
MBR 03.06.2011 Lots of changes
|
|
|
|
*/
|
|
|
|
utl_http.set_detailed_excp_support (enable => true);
|
|
utl_http.set_response_error_check (false);
|
|
|
|
utl_http.set_persistent_conn_support (true, 10);
|
|
|
|
debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count);
|
|
|
|
-- support for HTTPS
|
|
if instr(lower(p_url),'https') = 1 then
|
|
utl_http.set_wallet (p_wallet_path, p_wallet_password);
|
|
end if;
|
|
|
|
-- support for proxy server
|
|
if p_proxy_server is not null then
|
|
utl_http.set_proxy (p_proxy_server);
|
|
end if;
|
|
|
|
------------
|
|
-- Request 1
|
|
------------
|
|
|
|
debug_pkg.printf(' ');
|
|
debug_pkg.printf(p_url);
|
|
|
|
l_req := utl_http.begin_request(p_url);
|
|
|
|
l_resp := utl_http.get_response (l_req);
|
|
|
|
debug_response (l_resp);
|
|
|
|
if l_resp.status_code = utl_http.HTTP_UNAUTHORIZED then
|
|
|
|
l_authenticate_with_ntlm := headers_contain_value (l_resp, 'WWW-Authenticate', 'NTLM');
|
|
|
|
utl_http.end_response (l_resp);
|
|
|
|
if l_authenticate_with_ntlm then
|
|
|
|
l_negotiate_message := 'NTLM ' || ntlm_util_pkg.get_negotiate_message(p_username);
|
|
-- need to send negotiation message
|
|
|
|
debug_pkg.printf('Negotiate message: %1', l_negotiate_message);
|
|
|
|
------------
|
|
-- Request 2
|
|
------------
|
|
|
|
debug_pkg.printf(' ');
|
|
debug_pkg.printf(p_url);
|
|
l_req := utl_http.begin_request(p_url);
|
|
utl_http.set_header (l_req, 'Authorization', l_negotiate_message);
|
|
|
|
l_resp := utl_http.get_response(l_req);
|
|
|
|
debug_response (l_resp);
|
|
|
|
if l_resp.status_code = utl_http.HTTP_UNAUTHORIZED then
|
|
|
|
-- received server challenge
|
|
utl_http.get_header_by_name(l_resp, 'WWW-Authenticate', l_value, 1);
|
|
|
|
utl_http.end_response(l_resp);
|
|
|
|
if substr(l_value, 1, 4) = 'NTLM' then
|
|
|
|
-- get value
|
|
l_value := substr(l_value, 6);
|
|
|
|
ntlm_util_pkg.parse_challenge_message (l_value, l_server_challenge, l_negotiate_flags);
|
|
|
|
l_authenticate_message := 'NTLM ' || ntlm_util_pkg.get_authenticate_message(p_username, p_password, l_server_challenge, l_negotiate_flags);
|
|
debug_pkg.printf('Authenticate message: "%1"', l_authenticate_message);
|
|
|
|
------------
|
|
-- Request 3
|
|
------------
|
|
|
|
-- sending NTLM message 3
|
|
debug_pkg.printf(' ');
|
|
debug_pkg.printf(p_url);
|
|
l_req := utl_http.begin_request(p_url);
|
|
|
|
utl_http.set_header (l_req, 'Connection', 'close');
|
|
utl_http.set_header (l_req, 'Authorization', l_authenticate_message);
|
|
|
|
l_resp := utl_http.get_response (l_req);
|
|
|
|
debug_response (l_resp);
|
|
|
|
-- this is already done inside debug_response
|
|
--l_returnvalue := get_response_body (l_resp);
|
|
|
|
utl_http.end_response(l_resp);
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
else
|
|
debug_pkg.printf('Server is not configured with NTLM security (missing "WWW-Authenticate: NTLM" header).');
|
|
end if;
|
|
|
|
end if;
|
|
|
|
|
|
utl_http.close_persistent_conns;
|
|
debug_pkg.printf('Persistent connection count (should be zero): %1', utl_http.get_persistent_conn_count);
|
|
|
|
return l_returnvalue;
|
|
|
|
end get_response_blob;
|
|
|
|
|
|
function get_response_clob (p_url in varchar2,
|
|
p_username in varchar2,
|
|
p_password in varchar2,
|
|
p_wallet_path in varchar2 := null,
|
|
p_wallet_password in varchar2 := null,
|
|
p_proxy_server in varchar2 := null) return clob
|
|
as
|
|
|
|
l_req utl_http.req;
|
|
l_resp utl_http.resp;
|
|
l_returnvalue clob;
|
|
|
|
l_authenticate_with_ntlm boolean;
|
|
|
|
l_name varchar2(500);
|
|
l_value varchar2(500);
|
|
|
|
l_ntlm_message varchar2(500);
|
|
|
|
l_negotiate_message varchar2(500);
|
|
|
|
l_server_challenge raw(4000);
|
|
l_negotiate_flags raw(4000);
|
|
|
|
l_authenticate_message varchar2(500);
|
|
|
|
|
|
function get_response_body (p_resp in out utl_http.resp) return clob
|
|
as
|
|
l_data string_util_pkg.t_max_pl_varchar2;
|
|
l_returnvalue clob;
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: get the response body as a clob
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 24.06.2011 Created
|
|
|
|
*/
|
|
|
|
begin
|
|
loop
|
|
utl_http.read_text(r => p_resp, data => l_data);
|
|
l_returnvalue := l_returnvalue || l_data;
|
|
end loop;
|
|
exception
|
|
when utl_http.end_of_body then
|
|
null;
|
|
end;
|
|
|
|
return l_returnvalue;
|
|
|
|
end get_response_body;
|
|
|
|
|
|
procedure debug_response (p_resp in out utl_http.resp)
|
|
as
|
|
l_name varchar2(255);
|
|
l_value varchar2(2000);
|
|
l_body clob;
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: print debug info about the response
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 24.06.2011 Created
|
|
|
|
*/
|
|
|
|
debug_pkg.printf('Response Status Code: %1', p_resp.status_code);
|
|
|
|
for i in 1 .. utl_http.get_header_count (p_resp) loop
|
|
utl_http.get_header (p_resp, i, l_name, l_value);
|
|
debug_pkg.printf('#%1 %2 : %3', i, l_name, l_value);
|
|
end loop;
|
|
|
|
l_returnvalue := get_response_body (p_resp);
|
|
|
|
debug_pkg.printf('Body length = %1', dbms_lob.getlength (l_returnvalue));
|
|
debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count);
|
|
|
|
end debug_response;
|
|
|
|
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: Get response clob from URL
|
|
|
|
Remarks: see http://davenport.sourceforge.net/ntlm.html#ntlmHttpAuthentication
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
FDL 11.05.2011 Created
|
|
MBR 03.06.2011 Lots of changes
|
|
|
|
*/
|
|
|
|
utl_http.set_detailed_excp_support (enable => true);
|
|
utl_http.set_response_error_check (false);
|
|
|
|
utl_http.set_persistent_conn_support (true, 10);
|
|
|
|
debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count);
|
|
|
|
-- support for HTTPS
|
|
if instr(lower(p_url),'https') = 1 then
|
|
utl_http.set_wallet (p_wallet_path, p_wallet_password);
|
|
end if;
|
|
|
|
-- support for proxy server
|
|
if p_proxy_server is not null then
|
|
utl_http.set_proxy (p_proxy_server);
|
|
end if;
|
|
|
|
------------
|
|
-- Request 1
|
|
------------
|
|
|
|
debug_pkg.printf(' ');
|
|
debug_pkg.printf(p_url);
|
|
|
|
l_req := utl_http.begin_request(p_url);
|
|
|
|
l_resp := utl_http.get_response (l_req);
|
|
|
|
debug_response (l_resp);
|
|
|
|
if l_resp.status_code = utl_http.HTTP_UNAUTHORIZED then
|
|
|
|
l_authenticate_with_ntlm := headers_contain_value (l_resp, 'WWW-Authenticate', 'NTLM');
|
|
|
|
utl_http.end_response (l_resp);
|
|
|
|
if l_authenticate_with_ntlm then
|
|
|
|
l_negotiate_message := 'NTLM ' || ntlm_util_pkg.get_negotiate_message(p_username);
|
|
-- need to send negotiation message
|
|
|
|
debug_pkg.printf('Negotiate message: %1', l_negotiate_message);
|
|
|
|
------------
|
|
-- Request 2
|
|
------------
|
|
|
|
debug_pkg.printf(' ');
|
|
debug_pkg.printf(p_url);
|
|
l_req := utl_http.begin_request(p_url);
|
|
utl_http.set_header (l_req, 'Authorization', l_negotiate_message);
|
|
|
|
l_resp := utl_http.get_response(l_req);
|
|
|
|
debug_response (l_resp);
|
|
|
|
if l_resp.status_code = utl_http.HTTP_UNAUTHORIZED then
|
|
|
|
-- received server challenge
|
|
utl_http.get_header_by_name(l_resp, 'WWW-Authenticate', l_value, 1);
|
|
|
|
utl_http.end_response(l_resp);
|
|
|
|
if substr(l_value, 1, 4) = 'NTLM' then
|
|
|
|
-- get value
|
|
l_value := substr(l_value, 6);
|
|
|
|
ntlm_util_pkg.parse_challenge_message (l_value, l_server_challenge, l_negotiate_flags);
|
|
|
|
l_authenticate_message := 'NTLM ' || ntlm_util_pkg.get_authenticate_message(p_username, p_password, l_server_challenge, l_negotiate_flags);
|
|
debug_pkg.printf('Authenticate message: "%1"', l_authenticate_message);
|
|
|
|
------------
|
|
-- Request 3
|
|
------------
|
|
|
|
-- sending NTLM message 3
|
|
debug_pkg.printf(' ');
|
|
debug_pkg.printf(p_url);
|
|
l_req := utl_http.begin_request(p_url);
|
|
|
|
utl_http.set_header (l_req, 'Connection', 'close');
|
|
utl_http.set_header (l_req, 'Authorization', l_authenticate_message);
|
|
|
|
l_resp := utl_http.get_response (l_req);
|
|
|
|
debug_response (l_resp);
|
|
|
|
-- this is already done inside debug_response
|
|
--l_returnvalue := get_response_body (l_resp);
|
|
|
|
utl_http.end_response(l_resp);
|
|
|
|
end if;
|
|
|
|
end if;
|
|
|
|
else
|
|
debug_pkg.printf('Server is not configured with NTLM security (missing "WWW-Authenticate: NTLM" header).');
|
|
end if;
|
|
|
|
end if;
|
|
|
|
|
|
utl_http.close_persistent_conns;
|
|
debug_pkg.printf('Persistent connection count (should be zero): %1', utl_http.get_persistent_conn_count);
|
|
|
|
return l_returnvalue;
|
|
|
|
|
|
end get_response_clob;
|
|
|
|
|
|
function begin_request (p_url in varchar2,
|
|
p_username in varchar2,
|
|
p_password in varchar2,
|
|
p_wallet_path in varchar2 := null,
|
|
p_wallet_password in varchar2 := null,
|
|
p_proxy_server in varchar2 := null) return varchar2
|
|
as
|
|
|
|
l_method varchar2(255) := 'GET';
|
|
|
|
l_req utl_http.req;
|
|
l_resp utl_http.resp;
|
|
l_response_body clob;
|
|
|
|
l_returnvalue varchar2(2000);
|
|
|
|
l_name varchar2(500);
|
|
l_value varchar2(500);
|
|
|
|
l_ntlm_message varchar2(500);
|
|
l_negotiate_message varchar2(500);
|
|
l_server_challenge raw(4000);
|
|
l_negotiate_flags raw(4000);
|
|
l_authenticate_message varchar2(500);
|
|
|
|
|
|
function get_response_body (p_resp in out utl_http.resp) return clob
|
|
as
|
|
l_data string_util_pkg.t_max_pl_varchar2;
|
|
l_returnvalue clob;
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: get the response body as a clob
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 24.06.2011 Created
|
|
|
|
*/
|
|
|
|
begin
|
|
loop
|
|
utl_http.read_text(r => p_resp, data => l_data);
|
|
l_returnvalue := l_returnvalue || l_data;
|
|
end loop;
|
|
exception
|
|
when utl_http.end_of_body then
|
|
null;
|
|
end;
|
|
|
|
return l_returnvalue;
|
|
|
|
end get_response_body;
|
|
|
|
|
|
procedure debug_response (p_resp in out utl_http.resp)
|
|
as
|
|
l_name varchar2(255);
|
|
l_value varchar2(2000);
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: print debug info about the response
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 24.06.2011 Created
|
|
|
|
*/
|
|
|
|
debug_pkg.printf('Response Status Code: %1', p_resp.status_code);
|
|
|
|
for i in 1 .. utl_http.get_header_count (p_resp) loop
|
|
utl_http.get_header (p_resp, i, l_name, l_value);
|
|
debug_pkg.printf('#%1 %2 : %3', i, l_name, l_value);
|
|
end loop;
|
|
|
|
l_response_body := get_response_body (p_resp);
|
|
|
|
debug_pkg.printf('Body length = %1', dbms_lob.getlength (l_response_body));
|
|
debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count);
|
|
|
|
end debug_response;
|
|
|
|
|
|
begin
|
|
|
|
/*
|
|
|
|
Purpose: begin NTLM request
|
|
|
|
Remarks: it is assumed that the request will be against an NTLM-protected URL, so the initial request (where the server responds with WWW-Authenticate: NTLM) is skipped
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 24.06.2011 Created
|
|
|
|
*/
|
|
|
|
utl_http.set_detailed_excp_support (enable => true);
|
|
utl_http.set_response_error_check (enable => false);
|
|
|
|
utl_http.set_persistent_conn_support (true, 10);
|
|
|
|
-- support for HTTPS
|
|
if instr(lower(p_url),'https') = 1 then
|
|
utl_http.set_wallet (p_wallet_path, p_wallet_password);
|
|
end if;
|
|
|
|
-- support for proxy server
|
|
if p_proxy_server is not null then
|
|
utl_http.set_proxy (p_proxy_server);
|
|
end if;
|
|
|
|
|
|
l_negotiate_message := 'NTLM ' || ntlm_util_pkg.get_negotiate_message (p_username);
|
|
|
|
debug_pkg.printf('Negotiate Message: %1', l_negotiate_message);
|
|
|
|
------------
|
|
-- Request 1
|
|
------------
|
|
|
|
debug_pkg.printf(' ');
|
|
debug_pkg.printf(l_method || ' ' || p_url);
|
|
|
|
l_req := utl_http.begin_request (p_url, l_method);
|
|
utl_http.set_header (l_req, 'Authorization', l_negotiate_message);
|
|
|
|
l_resp := utl_http.get_response(l_req);
|
|
|
|
debug_response (l_resp);
|
|
|
|
if l_resp.status_code = utl_http.http_unauthorized then
|
|
|
|
-- received server challenge
|
|
utl_http.get_header_by_name (l_resp, 'WWW-Authenticate', l_value, 1);
|
|
utl_http.end_response (l_resp);
|
|
|
|
if substr(l_value, 1, 4) = 'NTLM' then
|
|
|
|
l_value := substr(l_value, 6);
|
|
|
|
ntlm_util_pkg.parse_challenge_message (l_value, l_server_challenge, l_negotiate_flags);
|
|
l_authenticate_message := 'NTLM ' || ntlm_util_pkg.get_authenticate_message (p_username, p_password, l_server_challenge, l_negotiate_flags);
|
|
|
|
debug_pkg.printf('Authenticate Message: "%1"', l_authenticate_message);
|
|
|
|
-- this is what needs to be passed as the Authorization header in the next call (and TCP connection must be kept persistent)
|
|
l_returnvalue := l_authenticate_message;
|
|
|
|
end if;
|
|
|
|
else
|
|
|
|
utl_http.end_response (l_resp);
|
|
l_returnvalue := null;
|
|
|
|
end if;
|
|
|
|
return l_returnvalue;
|
|
|
|
end begin_request;
|
|
|
|
|
|
procedure end_request
|
|
as
|
|
begin
|
|
|
|
|
|
/*
|
|
|
|
Purpose: end NTLM request
|
|
|
|
Remarks:
|
|
|
|
Who Date Description
|
|
------ ---------- --------------------------------
|
|
MBR 24.06.2011 Created
|
|
|
|
*/
|
|
|
|
debug_pkg.printf('Persistent connection count: %1', utl_http.get_persistent_conn_count);
|
|
utl_http.close_persistent_conns;
|
|
debug_pkg.printf('Persistent connection count (should be zero): %1', utl_http.get_persistent_conn_count);
|
|
|
|
end end_request;
|
|
|
|
|
|
|
|
end ntlm_http_pkg;
|
|
/
|
|
|