fpc/packages/libmicrohttpd/examples/tlsauthentication.pp
michael de800d14cb * Added libmicrohttpd, from Silvio Clecio.
git-svn-id: trunk@32067 -
2015-10-17 08:13:58 +00:00

235 lines
6.0 KiB
ObjectPascal

(* Feel free to use this example code in any way
you see fit (Public Domain) *)
// Original example: https://gnunet.org/svn/libmicrohttpd/doc/examples/tlsauthentication.c
(*
* Generate PEM files for test this example:
*
* openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout key.pem -out cert.pem
*
* or
*
* openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout server.key -out server.pem
*)
program tlsauthentication;
{$mode objfpc}{$H+}
uses
SysUtils, ctypes, cmem, cutils, libmicrohttpd;
const
PORT = 8888;
REALM = '"Maintenance"';
USER = 'a legitimate user';
PASSWORD = 'and his password';
SERVERKEYFILE = 'server.key';
SERVERCERTFILE = 'server.pem';
function iif(c: cbool; t, f: culong): culong;
begin
if c then
Result := t
else
Result := f;
end;
function string_to_base64(message: Pcchar): Pcchar;
var
lookup: Pcchar = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/';
l: culong;
i: cint;
tmp: Pcchar;
len: SizeInt;
begin
len := strlen(message);
tmp := Malloc(len * 2);
if nil = tmp then
Exit(tmp);
tmp[0] := #0;
i := 0;
while i < len do
begin
l := (culong(message[i]) shl 16)
or iif((i + 1) < len, culong(message[i + 1]) shl 8, 0)
or iif((i + 2) < len, culong(message[i + 2]), 0);
strncat(tmp, @lookup[(l shr 18) and $3F], 1);
strncat(tmp, @lookup[(l shr 12) and $3F], 1);
if i + 1 < len then
strncat(tmp, @lookup[(l shr 6) and $3F], 1);
if i + 2 < len then
strncat(tmp, @lookup[l and $3F], 1);
i += 3;
end;
if (len mod 3 = 1) then
strncat(tmp, '===', 3 - len mod 3);
Result := tmp;
end;
function get_file_size(filename: Pcchar): clong;
var
fp: FILEptr;
size: clong;
begin
fp := fopen(filename, fopenread);
if Assigned(fp) then
begin
if 0 <> fseek(fp, 0, SEEK_END) then
size := 0;
size := ftell(fp);
if -1 = size then
size := 0;
fclose(fp);
Result := size;
end
else
Result := 0;
end;
function load_file(filename: Pcchar): Pcchar;
var
fp: FILEptr;
buffer: Pcchar;
size: clong;
begin
size := get_file_size(filename);
if size = 0 then
Exit(nil);
fp := fopen(filename, fopenread);
if not Assigned(fp) then
Exit(nil);
buffer := Malloc(size);
if not Assigned(buffer) then
begin
fclose(fp);
Exit(nil);
end;
if size <> fread(buffer, 1, size, fp) then
begin
free(buffer);
buffer := nil;
end;
fclose(fp);
Result := buffer;
end;
function ask_for_authentication(connection: PMHD_Connection;
realm: Pcchar): cint; cdecl;
var
ret: cint;
response: PMHD_Response;
headervalue: Pcchar;
strbase: Pcchar = 'Basic realm=';
begin
response := MHD_create_response_from_buffer(0, nil, MHD_RESPMEM_PERSISTENT);
if not Assigned(response) then
Exit(MHD_NO);
headervalue := Malloc(strlen(strbase) + strlen(realm) + 1);
if not Assigned(headervalue) then
Exit(MHD_NO);
strcpy(headervalue, strbase);
strcat(headervalue, realm);
ret := MHD_add_response_header(response, 'WWW-Authenticate', headervalue);
Free(headervalue);
if ret <> 1 then
begin
MHD_destroy_response(response);
Exit(MHD_NO);
end;
ret := MHD_queue_response(connection, MHD_HTTP_UNAUTHORIZED, response);
MHD_destroy_response(response);
Result := ret;
end;
function is_authenticated(connection: PMHD_Connection;
username, password: Pcchar): cint; cdecl;
var
headervalue: Pcchar;
expected_b64, expected: Pcchar;
strbase: Pcchar = 'Basic ';
authenticated: cint;
begin
headervalue := MHD_lookup_connection_value(connection, MHD_HEADER_KIND,
'Authorization');
if nil = headervalue then
Exit(0);
if 0 <> strncmp(headervalue, strbase, strlen(strbase)) then
Exit(0);
expected := malloc(strlen(username) + 1 + strlen(password) + 1);
if nil = expected then
Exit(0);
strcpy(expected, username);
strcat(expected, ':');
strcat(expected, password);
expected_b64 := string_to_base64(expected);
free(expected);
if nil = expected_b64 then
Exit(0);
authenticated := cint(strcomp(headervalue + strlen(strbase), expected_b64) = 0);
Free(expected_b64);
Result := authenticated;
end;
function secret_page(connection: PMHD_Connection): cint; cdecl;
var
ret: cint;
response: PMHD_Response;
page: Pcchar = '<html><body>A secret.</body></html>';
begin
response := MHD_create_response_from_buffer(strlen(page), Pointer(page),
MHD_RESPMEM_PERSISTENT);
if not Assigned(response) then
Exit(MHD_NO);
ret := MHD_queue_response(connection, MHD_HTTP_OK, response);
MHD_destroy_response(response);
Result := ret;
end;
function answer_to_connection(cls: Pointer; connection: PMHD_Connection;
url: Pcchar; method: Pcchar; version: Pcchar; upload_data: Pcchar;
upload_data_size: Psize_t; con_cls: PPointer): cint; cdecl;
begin
if 0 <> strcomp(method, 'GET') then
Exit(MHD_NO);
if nil = con_cls^ then
begin
con_cls^ := connection;
Exit(MHD_YES);
end;
if is_authenticated(connection, USER, PASSWORD) <> 1 then
Exit(ask_for_authentication(connection, REALM));
Result := secret_page(connection);
end;
var
daemon: PMHD_Daemon;
key_pem: Pcchar;
cert_pem: Pcchar;
begin
key_pem := load_file(SERVERKEYFILE);
cert_pem := load_file(SERVERCERTFILE);
if (key_pem = nil) or (cert_pem = nil) then
begin
WriteLn('The key/certificate files could not be read.');
Halt(1);
end;
daemon := MHD_start_daemon(MHD_USE_SELECT_INTERNALLY or MHD_USE_SSL, PORT,
nil, nil, @answer_to_connection, nil, MHD_OPTION_HTTPS_MEM_KEY, key_pem,
MHD_OPTION_HTTPS_MEM_CERT, cert_pem, MHD_OPTION_END);
if nil = daemon then
begin
WriteLn(cert_pem);
Free(key_pem);
Free(cert_pem);
Halt(1);
end;
ReadLn;
MHD_stop_daemon(daemon);
Free(key_pem);
Free(cert_pem);
end.