From 9e1030ef6e80e101b0e572e3746ab84dde244a01 Mon Sep 17 00:00:00 2001 From: Hans Nilsson Date: Wed, 13 Nov 2013 10:39:39 +0100 Subject: ftps: working --- lib/inets/src/ftp/ftp.erl | 218 +++++++++++++++--------------- lib/inets/test/ftp_SUITE.erl | 49 ++++--- lib/inets/test/ftp_SUITE_data/vsftpd.conf | 10 +- 3 files changed, 146 insertions(+), 131 deletions(-) (limited to 'lib/inets') diff --git a/lib/inets/src/ftp/ftp.erl b/lib/inets/src/ftp/ftp.erl index 98f4276eeb..df2ee7d201 100644 --- a/lib/inets/src/ftp/ftp.erl +++ b/lib/inets/src/ftp/ftp.erl @@ -18,7 +18,7 @@ %% %% %% Description: This module implements an ftp client, RFC 959. -%% It also supports ipv6 RFC 2428. +%% It also supports ipv6 RFC 2428 and starttls RFC 4217. -module(ftp). @@ -92,7 +92,7 @@ ipfamily, % inet | inet6 | inet6fb4 progress = ignore, % ignore | pid() dtimeout = ?DATA_ACCEPT_TIMEOUT, % non_neg_integer() | infinity - tls_upgrading_data_connection = {false,undefined} + tls_upgrading_data_connection = false }). @@ -101,6 +101,8 @@ -type common_reason() :: 'econn' | 'eclosed' | term(). -type file_write_error_reason() :: term(). % See file:write for more info +%%-define(DBG(F,A), 'n/a'). +-define(DBG(F,A), io:format(F,A)). %%%========================================================================= %%% API - CLIENT FUNCTIONS @@ -1315,61 +1317,63 @@ handle_info(timeout, State) -> {noreply, State}; %%% Data socket messages %%% -handle_info({tcp, Socket, Data}, - #state{dsock = {tcp,Socket}, - caller = {recv_file, Fd}} = State0) -> - io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), +handle_info({Trpt, Socket, Data}, + #state{dsock = {Trpt,Socket}, + caller = {recv_file, Fd}} = State0) when Trpt==tcp;Trpt==ssl -> + ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), file_write(binary_to_list(Data), Fd), progress_report({binary, Data}, State0), State = activate_data_connection(State0), {noreply, State}; -handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}, client = From, +handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}, client = From, caller = recv_chunk} - = State) -> - io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State]), + = State) when Trpt==tcp;Trpt==ssl -> + ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State]), gen_server:reply(From, {ok, Data}), {noreply, State#state{client = undefined, data = <<>>}}; -handle_info({tcp, Socket, Data}, #state{dsock = {tcp,Socket}} = State0) -> - io:format('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), +handle_info({Trpt, Socket, Data}, #state{dsock = {Trpt,Socket}} = State0) when Trpt==tcp;Trpt==ssl -> + ?DBG('L~p --data ~p ----> ~s~p~n',[?LINE,Socket,Data,State0]), State = activate_data_connection(State0), {noreply, State#state{data = <<(State#state.data)/binary, Data/binary>>}}; -handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, +handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = {recv_file, Fd}} - = State) -> + = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> file_close(Fd), progress_report({transfer_size, 0}, State), activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, data = <<>>}}; -handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, client = From, +handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, client = From, caller = recv_chunk} - = State) -> + = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> gen_server:reply(From, ok), {noreply, State#state{dsock = undefined, client = undefined, data = <<>>, caller = undefined, chunk = false}}; -handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, caller = recv_bin, - data = Data} = State) -> +handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, caller = recv_bin, + data = Data} = State) + when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, data = <<>>, caller = {recv_bin, Data}}}; -handle_info({tcp_closed, Socket}, #state{dsock = {tcp,Socket}, data = Data, +handle_info({Cls, Socket}, #state{dsock = {Trpt,Socket}, data = Data, caller = {handle_dir_result, Dir}} - = State) -> + = State) when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> activate_ctrl_connection(State), {noreply, State#state{dsock = undefined, caller = {handle_dir_result, Dir, Data}, % data = <>}}; data = <<>>}}; - -handle_info({tcp_error, Socket, Reason}, #state{dsock = Socket, - client = From} = State) -> + +handle_info({Err, Socket, Reason}, #state{dsock = {Trpt,Socket}, + client = From} = State) + when {Err,Trpt}=={tcp_error,tcp} ; {Err,Trpt}=={ssl_error,ssl} -> gen_server:reply(From, {error, Reason}), close_data_connection(State), {noreply, State#state{dsock = undefined, client = undefined, @@ -1383,7 +1387,7 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket}, ctrl_data = {CtrlData, AccLines, LineStatus}} = State) -> - io:format('--ctrl ~p ----> ~s~p~n',[Socket,<>,State]), + ?DBG('--ctrl ~p ----> ~s~p~n',[Socket,<>,State]), case ftp_response:parse_lines(<>, AccLines, LineStatus) of {ok, Lines, NextMsgData} -> @@ -1397,34 +1401,31 @@ handle_info({Transport, Socket, Data}, #state{csock = {Transport, Socket}, ctrl_data = {NextMsgData, [], start}}}; _ -> - io:format(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]), + ?DBG(' ...handle_ctrl_result(~p,...) ctrl_data=~p~n',[CtrlResult,{NextMsgData, [], start}]), handle_ctrl_result(CtrlResult, State#state{ctrl_data = {NextMsgData, [], start}}) end; {continue, NewCtrlData} -> - io:format(' ...Continue... ctrl_data=~p~n',[NewCtrlData]), + ?DBG(' ...Continue... ctrl_data=~p~n',[NewCtrlData]), activate_ctrl_connection(State), {noreply, State#state{ctrl_data = NewCtrlData}} end; %% If the server closes the control channel it is %% the expected behavior that connection process terminates. -handle_info({tcp_closed, Socket}, #state{csock = {tcp, Socket}}) -> +handle_info({Cls, Socket}, #state{csock = {Trpt, Socket}}) + when {Cls,Trpt}=={tcp_closed,tcp} ; {Cls,Trpt}=={ssl_closed,ssl} -> exit(normal); %% User will get error message from terminate/2 -handle_info({ssl_closed, Socket}, #state{csock = {ssl, Socket}}) -> - exit(normal); -handle_info({tcp_error, Socket, Reason}, _) -> +handle_info({Err, Socket, Reason}, _) when Err==tcp_error ; Err==ssl_error -> Report = - io_lib:format("tcp_error on socket: ~p for reason: ~p~n", - [Socket, Reason]), + io_lib:format("~p on socket: ~p for reason: ~p~n", + [Err, Socket, Reason]), error_logger:error_report(Report), %% If tcp does not work the only option is to terminate, %% this is the expected behavior under these circumstances. exit(normal); %% User will get error message from terminate/2 -handle_info({ssl_error, _Socket, _Reason}, _) -> - exit(normal); %% TODO?? %% Monitor messages - if the process owning the ftp connection goes %% down there is no point in continuing. @@ -1592,44 +1593,36 @@ handle_user_account(Acc, State) -> %%-------------------------------------------------------------------------- %% handle_ctrl_result %%-------------------------------------------------------------------------- -%%-------------------------------------------------------------------------- -%% Handling of control connection setup -handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz, _}} = State) -> - send_ctrl_message(State, mk_cmd("PROT P", [])), - activate_ctrl_connection(State), - {noreply, State#state{tls_upgrading_data_connection = {true, prot, undefined}}}; - -handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot, NextAction}, - dsock = {tcp, Socket}, client = From, - tls_options = TLSOptions} = State0) -> - io:format('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), - case ssl:connect(Socket, TLSOptions) of - {ok, TLSSocket} -> - State = State0#state{dsock = {ssl, TLSSocket}, - tls_upgrading_data_connection = {false, undefined}}, - next_action_after_tls_upgrade(NextAction, State); - {error, _} = Error -> - gen_server:reply(From, {Error, self()}), - {stop, normal, State0#state{client = undefined, - caller = undefined}} - end; - handle_ctrl_result({tls_upgrade, _}, #state{csock = {tcp, Socket}, tls_options = TLSOptions, caller = open, client = From} - = State) -> - io:format('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]), + = State0) -> + ?DBG('<--ctrl ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), case ssl:connect(Socket, TLSOptions) of {ok, TLSSocket} -> - gen_server:reply(From, {ok, self()}), - {noreply, State#state{csock = {ssl, TLSSocket}, - client = undefined, - caller = undefined }}; + State = State0#state{csock = {ssl,TLSSocket}}, + send_ctrl_message(State, mk_cmd("PBSZ 0", [])), + activate_ctrl_connection(State), + {noreply, State#state{tls_upgrading_data_connection = {true, pbsz}} }; {error, _} = Error -> gen_server:reply(From, {Error, self()}), - {stop, normal, State#state{client = undefined, - caller = undefined}} + {stop, normal, State0#state{client = undefined, + caller = undefined, + tls_upgrading_data_connection = false}} end; + +handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, pbsz}} = State) -> + send_ctrl_message(State, mk_cmd("PROT P", [])), + activate_ctrl_connection(State), + {noreply, State#state{tls_upgrading_data_connection = {true, prot}}}; + +handle_ctrl_result({pos_compl, _}, #state{tls_upgrading_data_connection = {true, prot}, + client = From} = State) -> + gen_server:reply(From, {ok, self()}), + {noreply, State#state{client = undefined, + caller = undefined, + tls_upgrading_data_connection = false}}; + handle_ctrl_result({pos_compl, _}, #state{caller = open, client = From} = State) -> gen_server:reply(From, {ok, self()}), @@ -1676,7 +1669,7 @@ handle_ctrl_result({pos_compl, Lines}, #state{mode = passive, ipfamily = inet, client = From, - caller = {setup_data_connection, Caller}, + caller = {setup_data_connection, Caller}, timeout = Timeout} = State) -> {_, [?LEFT_PAREN | Rest]} = @@ -1688,9 +1681,10 @@ handle_ctrl_result({pos_compl, Lines}, string:tokens(NewPortAddr, [$,])), IP = {A1, A2, A3, A4}, Port = (P1 * 256) + P2, -io:format('tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]), + + ?DBG('<--data tcp connect to ~p:~p, Caller=~p~n',[IP,Port,Caller]), case connect(IP, Port, Timeout, State) of - {ok, _, Socket} -> + {ok, _, Socket} -> handle_caller(State#state{caller = Caller, dsock = {tcp,Socket}}); {error, _Reason} = Error -> gen_server:reply(From, Error), @@ -2132,25 +2126,46 @@ connect2(Host, Port, IpFam, Timeout) -> accept_data_connection(#state{mode = active, dtimeout = DTimeout, + tls_options = TLSOptions, dsock = {lsock, LSock}} = State0) -> case gen_tcp:accept(LSock, DTimeout) of + {ok, Socket} when is_list(TLSOptions) -> + gen_tcp:close(LSock), + ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State0]), + case ssl:connect(Socket, TLSOptions) of + {ok, TLSSocket} -> + {ok, State0#state{dsock={ssl,TLSSocket}}}; + {error, Reason} -> + {error, {ssl_connect_failed, Reason}} + end; {ok, Socket} -> gen_tcp:close(LSock), - maybe_requests_tls_upgrade(State0#state{dsock = {tcp, Socket}}); + {ok, State0#state{dsock={tcp,Socket}}}; {error, Reason} -> {error, {data_connect_failed, Reason}} end; +accept_data_connection(#state{mode = passive, + dsock = {tcp,Socket}, + tls_options = TLSOptions} = State) when is_list(TLSOptions) -> + ?DBG('<--data ssl:connect(~p, ~p)~n~p~n',[Socket,TLSOptions,State]), + case ssl:connect(Socket, TLSOptions) of + {ok, TLSSocket} -> + {ok, State#state{dsock={ssl,TLSSocket}}}; + {error, Reason} -> + {error, {ssl_connect_failed, Reason}} + end; accept_data_connection(#state{mode = passive} = State) -> - maybe_requests_tls_upgrade(State). + {ok,State}. + send_ctrl_message(S=#state{csock = Socket, verbose = Verbose}, Message) -> verbose(lists:flatten(Message),Verbose,send), - io:format('<--ctrl ~p ---- ~s~p~n',[Socket,Message,S]), + ?DBG('<--ctrl ~p ---- ~s~p~n',[Socket,Message,S]), send_message(Socket, Message). send_data_message(S=#state{dsock = Socket}, Message) -> - io:format('<==data ~p ==== ~s~p~n',[Socket,Message,S]), + ?DBG('<==data ~p ==== ~s~p~n',[Socket,Message,S]), case send_message(Socket, Message) of ok -> ok; @@ -2173,34 +2188,28 @@ activate_ctrl_connection(#state{csock = Socket, ctrl_data = {<<>>, _, _}}) -> activate_ctrl_connection(#state{csock = Socket}) -> %% We have already received at least part of the next control message, %% that has been saved in ctrl_data, process this first. - self() ! {tcp, Socket, <<>>}. + self() ! {tcp, unwrap_socket(Socket), <<>>}. -activate_data_connection(#state{tls_upgrading_data_connection = {false, _}, - dsock = Socket} = State) -> +unwrap_socket({tcp,Socket}) -> Socket; +unwrap_socket({ssl,Socket}) -> Socket; +unwrap_socket(Socket) -> Socket. + + +activate_data_connection(#state{dsock = Socket} = State) -> activate_connection(Socket), - State; -activate_data_connection(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) -> - State#state{tls_upgrading_data_connection = {true, CTRL, {?MODULE,activate_data_connection,undefined}}}. + State. -activate_connection({tcp, Socket}) -> - inet:setopts(Socket, [{active, once}]); -activate_connection({ssl, Socket}) -> - ssl:setopts(Socket, [{active, once}]). +activate_connection({tcp, Socket}) -> inet:setopts(Socket, [{active, once}]); +activate_connection({ssl, Socket}) -> ssl:setopts(Socket, [{active, once}]). -close_ctrl_connection(#state{csock = undefined}) -> - ok; -close_ctrl_connection(#state{csock = Socket}) -> - close_connection(Socket). +close_ctrl_connection(#state{csock = undefined}) -> ok; +close_ctrl_connection(#state{csock = Socket}) -> close_connection(Socket). -close_data_connection(#state{dsock = undefined}) -> - ok; -close_data_connection(#state{dsock = Socket}) -> - close_connection(Socket). +close_data_connection(#state{dsock = undefined}) -> ok; +close_data_connection(#state{dsock = Socket}) -> close_connection(Socket). -close_connection({tcp, Socket}) -> - gen_tcp:close(Socket); -close_connection({ssl, Socket}) -> - ssl:close(Socket). +close_connection({tcp, Socket}) -> gen_tcp:close(Socket); +close_connection({ssl, Socket}) -> ssl:close(Socket). %% ------------ FILE HANDELING ---------------------------------------- send_file(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State, Fd) -> @@ -2318,14 +2327,11 @@ millisec_time() -> {A,B,C} = erlang:now(), A*1000000000+B*1000+(C div 1000). -peername({tcp, Socket}) -> - inet:peername(Socket); -peername({ssl, Socket}) -> - ssl:peername(Socket). -sockname({tcp, Socket}) -> - inet:peername(Socket); -sockname({ssl, Socket}) -> - ssl:peername(Socket). +peername({tcp, Socket}) -> inet:peername(Socket); +peername({ssl, Socket}) -> ssl:peername(Socket). + +sockname({tcp, Socket}) -> inet:peername(Socket); +sockname({ssl, Socket}) -> ssl:peername(Socket). maybe_tls_upgrade(Pid, undefined) -> {ok, Pid}; @@ -2333,18 +2339,6 @@ maybe_tls_upgrade(Pid, TLSOptions) -> catch ssl:start(), call(Pid, {open, tls_upgrade, TLSOptions}, plain). -maybe_requests_tls_upgrade(#state{tls_options=undefined} = State) -> - {ok, State}; -maybe_requests_tls_upgrade(State) -> - send_ctrl_message(State, mk_cmd("PBSZ 0", [])), - activate_ctrl_connection(State), - {ok, State#state{tls_upgrading_data_connection = {true, pbsz, undefined}}}. - -next_action_after_tls_upgrade({M, F, undefined}, State) -> - M:F(State); -next_action_after_tls_upgrade({M, F, A}, State) -> - M:F(State, A). - start_chunk(#state{tls_upgrading_data_connection = {true, CTRL, _}} = State) -> State#state{tls_upgrading_data_connection = {true, CTRL, ?MODULE, start_chunk, undefined}}; start_chunk(#state{client = From} = State) -> diff --git a/lib/inets/test/ftp_SUITE.erl b/lib/inets/test/ftp_SUITE.erl index fcee2eddde..dea147fb56 100644 --- a/lib/inets/test/ftp_SUITE.erl +++ b/lib/inets/test/ftp_SUITE.erl @@ -32,15 +32,18 @@ -compile(export_all). -define(FTP_USER, "anonymous"). --define(FTP_PASS, (fun({ok,__H}) -> "ftp_SUITE@" ++ __H; - (_) -> "ftp_SUITE@localhost" - end)(inet:gethostname()) +-define(FTP_PASS(Cmnt), (fun({ok,__H}) -> "ftp_SUITE_"++Cmnt++"@" ++ __H; + (_) -> "ftp_SUITE_"++Cmnt++"@localhost" + end)(inet:gethostname()) ). -define(BAD_HOST, "badhostname"). -define(BAD_USER, "baduser"). -define(BAD_DIR, "baddirectory"). +go() -> ct:run_test([{suite,"ftp_SUITE"}, {logdir,"LOG"}]). +gos() -> ct:run_test([{suite,"ftp_SUITE"}, {group,ftps_passive}, {logdir,"LOG"}]). + %%-------------------------------------------------------------------- %% Common Test interface functions ----------------------------------- %%-------------------------------------------------------------------- @@ -112,14 +115,24 @@ ftp_tests()-> [{"vsftpd", fun(__CONF__) -> %% make_config_file(vsftpd, Conf), - ConfFile = filename:join(?config(data_dir,__CONF__), "vsftpd.conf"), + DataDir = ?config(data_dir,__CONF__), + ConfFile = filename:join(DataDir, "vsftpd.conf"), AnonRoot = ?config(priv_dir,__CONF__), - Cmd = ["vsftpd \"",ConfFile,"\"", + Cmd = ["vsftpd "++filename:join(DataDir,"vsftpd.conf"), " -oftpd_banner=erlang_otp_testing", - " -oanon_root=\"",AnonRoot,"\"" + " -oanon_root=\"",AnonRoot,"\"", + " -orsa_cert_file=\"",filename:join(DataDir,"cert.pem"),"\"", + " -orsa_private_key_file=\"",filename:join(DataDir,"key.pem"),"\"" ], - ct:log("~s",[Cmd]), - case os:cmd(Cmd) of + Result = os:cmd(Cmd), + ct:log("Config file:~n~s~n~nServer start command:~n ~s~nResult:~n ~p", + [case file:read_file(ConfFile) of + {ok,X} -> X; + _ -> "" + end, + Cmd, Result + ]), + case Result of [] -> {ok,'dont care'}; [Msg] -> {error,Msg} end @@ -154,7 +167,7 @@ init_per_suite(Config) -> end_per_suite(Config) -> ps_ftpd(Config), -%% stop_ftpd(Config), + stop_ftpd(Config), ps_ftpd(Config), ok. @@ -171,19 +184,23 @@ init_per_testcase(Case, Config0) -> catch _:_-> ok end, + TLS = [{tls,[{reuse_sessions,true}]}], + ACTIVE = [{mode,active}], + PASSIVE = [{mode,passive}], + ExtraOpts = [verbose], Config = case Group of - ftp_active -> ftp__open(Config0, [{mode,active}]); - ftps_active -> ftp__open(Config0, [{mode,active}]); - ftp_passive -> ftp__open(Config0, [{mode,passive}]); - ftps_passive -> ftp__open(Config0, [{mode,passive}]) + ftp_active -> ftp__open(Config0, ACTIVE ++ExtraOpts); + ftps_active -> ftp__open(Config0, TLS++ ACTIVE ++ExtraOpts); + ftp_passive -> ftp__open(Config0, PASSIVE ++ExtraOpts); + ftps_passive -> ftp__open(Config0, TLS++PASSIVE ++ExtraOpts) end, case Case of user -> Config; bad_user -> Config; _ -> Pid = ?config(ftp,Config), - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS(atom_to_list(Group)++"-"++atom_to_list(Case)) ), ok = ftp:cd(Pid, ?config(priv_dir,Config)), Config end. @@ -199,7 +216,7 @@ end_per_testcase(_Case, Config) -> ftp__close(Config). user(doc) -> ["Open an ftp connection to a host, and logon as anonymous ftp, then logoff"]; user(Config) -> Pid = ?config(ftp, Config), - ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS), % logon + ok = ftp:user(Pid, ?FTP_USER, ?FTP_PASS("")),% logon ok = ftp:close(Pid), % logoff {error,eclosed} = ftp:pwd(Pid), % check logoff result ok. @@ -208,7 +225,7 @@ user(Config) -> bad_user(doc) -> ["Open an ftp connection to a host, and logon with bad user."]; bad_user(Config) -> Pid = ?config(ftp, Config), - {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS), + {error, euser} = ftp:user(Pid, ?BAD_USER, ?FTP_PASS("")), ok. %%------------------------------------------------------------------------- diff --git a/lib/inets/test/ftp_SUITE_data/vsftpd.conf b/lib/inets/test/ftp_SUITE_data/vsftpd.conf index a7b260282c..4133f8f3b1 100644 --- a/lib/inets/test/ftp_SUITE_data/vsftpd.conf +++ b/lib/inets/test/ftp_SUITE_data/vsftpd.conf @@ -2,8 +2,10 @@ listen=YES listen_port=9999 run_as_launching_user=YES -#ssl_enable=YES -#allow_anon_ssl=YES +ssl_enable=YES +allow_anon_ssl=YES + +#background=NO background=YES write_enable=YES @@ -14,5 +16,7 @@ anon_other_write_enable=YES anon_world_readable_only=NO #rsa_cert_file=cert.pem -#rsa_key_file=key.pem +#rsa_private_key_file exists, not rsa_key_file=key.pem +### Shouldn't be necessary.... +require_ssl_reuse=NO -- cgit v1.2.3