summaryrefslogtreecommitdiff
path: root/p11p-daemon/src/p11p_client.erl
diff options
context:
space:
mode:
Diffstat (limited to 'p11p-daemon/src/p11p_client.erl')
-rw-r--r--p11p-daemon/src/p11p_client.erl94
1 files changed, 58 insertions, 36 deletions
diff --git a/p11p-daemon/src/p11p_client.erl b/p11p-daemon/src/p11p_client.erl
index 87c2949..d6c73ac 100644
--- a/p11p-daemon/src/p11p_client.erl
+++ b/p11p-daemon/src/p11p_client.erl
@@ -1,20 +1,31 @@
%%% Copyright (c) 2019, Sunet.
%%% See LICENSE for licensing information.
-%% A client spawns an Erlang port running a proxy app, i.e. the
-%% 'remote' program from p11-kit.
-
-%% Receive p11 requests from p11p_server, forward them to the proxy app,
-%% wait for a reply. If a reply is received within a timeout period,
-%% proxy the reply to the requesting p11p_server. If the request
-%% times out, inform the manager (our parent).
+%% Spawn an Erlang port running a proxy app. We use the 'remote'
+%% program from p11-kit as the proxy app.
+
+%% Receive PKCS#11 requests from a p11p_server, forward them to the
+%% proxy app, wait for a reply. If a reply is received within a
+%% timeout period, proxy the reply to the requesting p11p_server. If
+%% the request times out, inform the manager (our parent) and exit.
+
+%% Track a subset of the PKCS#11 state in order to handle token
+%% restarts. We start in state 'started'. While in 'started', we allow
+%% only a few "opening" calls (Initialize, OpenSession and Login)
+%% through to the token. Corresponding "closing" calls (Finalize,
+%% CloseSession and Logout) are sent an immediate OK response without
+%% forwarding them to the token. Any other call is rejected by
+%% responding with an error. This should make well behaving P11
+%% applications be able to deal with us switching the token under
+%% their feet.
-module(p11p_client).
-behaviour(gen_server).
%% API.
-export([start_link/6]).
--export([request/2, stop/2]).
+-export([request/2, % Request from p11p-server.
+ stop/2]). % Manager stopping us.
-include("p11p_rpc.hrl").
@@ -23,7 +34,12 @@
code_change/3]).
%% Records and types.
--type token_state() :: started | initialized | session | loggedin | opact | finalized.
+-type token_state() :: started |
+ initialized |
+ session |
+ loggedin |
+ opact |
+ finalized.
-record(state, {
token :: string(), % Token name.
@@ -40,8 +56,8 @@
}).
%% API.
--spec start_link(atom(), string(), pid(), string(), list(), non_neg_integer()) ->
- {ok, pid()} | {error, term()}.
+-spec start_link(atom(), string(), pid(), string(), list(),
+ non_neg_integer()) -> {ok, pid()} | {error, term()}.
start_link(ServName, TokName, Server, ModPath, ModEnv, Timeout) ->
lager:info("~p: starting p11p_client for ~s", [self(), TokName]),
gen_server:start_link({local, ServName}, ?MODULE,
@@ -51,12 +67,13 @@ start_link(ServName, TokName, Server, ModPath, ModEnv, Timeout) ->
request(Client, Request) ->
gen_server:call(Client, {request, Request}).
-%% Use stop/1 instead of gen_server:stop/1 if you're uncertain whether
-%% we (Pid) are alive or not. An example of when that can happen is
-%% when the manager receives a server_event about a lost p11 app. If
-%% the server process terminated on request from us because we timed
-%% out on an rpc call, chances are that we have already terminated by
-%% the time the manager acts on the information about the lost app.
+%% You should invoke stop/1 instead of gen_server:stop/1 if you're
+%% uncertain whether we (Pid) are alive or not. An example of when
+%% that can happen is when the manager receives a server_event about a
+%% lost P11 app -- if the server process terminated on request from us
+%% because we timed out on an RPC call, chances are that we have
+%% already terminated by the time the manager acts on the information
+%% about the lost app.
stop(Pid, Reason) ->
gen_server:cast(Pid, {stop, Reason}).
@@ -72,13 +89,16 @@ init([TokName, Server, ModPath, ModEnv, Timeout]) ->
true = is_port(Port),
lager:debug("~p: ~s: new proxy app port: ~p", [self(), ProxyAppBinPath, Port]),
lager:debug("~p: ~s: module: ~s, env: ~p", [self(), ProxyAppBinPath, ModPath, ModEnv]),
- {ok, #state{port = Port, token = TokName, replyto = Server, timeout = Timeout}}.
+ {ok, #state{port = Port,
+ token = TokName,
+ replyto = Server,
+ timeout = Timeout}}.
handle_call({request, Request},
{FromPid, _Tag},
- S = #state{port = Port, send_count = Sent}) ->
+ State = #state{port = Port, send_count = Sent}) ->
case
- case S#state.p11state of
+ case State#state.p11state of
started ->
case p11p_rpc:req_id(Request) of
?P11_RPC_CALL_C_Logout -> ack;
@@ -96,9 +116,9 @@ handle_call({request, Request},
end
of
ack ->
- {reply, ack, S};
+ {reply, ack, State};
nack ->
- {reply, nack, S};
+ {reply, nack, State};
pass ->
lager:debug("~p: sending request from ~p to prxoy app ~p", [self(), FromPid, Port]),
D = p11p_rpc:serialise(Request),
@@ -110,9 +130,9 @@ handle_call({request, Request},
{reply,
{ok, size(Buf)},
- S#state{replyto = FromPid,
- timer = start_timer(S#state.timeout, Port),
- send_count = Sent + 1}}
+ State#state{replyto = FromPid,
+ timer = start_timer(State#state.timeout, Port),
+ send_count = Sent + 1}}
end;
handle_call(Call, _From, State) ->
@@ -126,12 +146,13 @@ handle_cast(Cast, State) ->
lager:debug("~p: unhandled cast: ~p~n", [self(), Cast]),
{noreply, State}.
-%% Receiving the very first response from proxy app since it was started.
+%% Receiving the very first octets from proxy app since it was started.
handle_info({Port, {data, Data}}, State)
when Port == State#state.port, State#state.response == undefined ->
case hd(Data) of % First octet is RPC protocol version.
?RPC_VERSION ->
- {noreply, response_in(State, p11p_rpc:new(), tl(Data))};
+ NewState = response_in(State, p11p_rpc:new(), tl(Data)),
+ {noreply, NewState};
BadVersion ->
lager:info("~p: ~p: invalid RPC version: ~p", [self(), Port,
BadVersion]),
@@ -139,17 +160,18 @@ handle_info({Port, {data, Data}}, State)
end;
%% Receiving more data from proxy app.
-handle_info({Port, {data, Data}}, #state{response = Msg} = State)
+handle_info({Port, {data, Data}}, State)
when Port == State#state.port ->
- {noreply, response_in(State, Msg, Data)};
+ NewState = response_in(State, State#state.response, Data),
+ {noreply, NewState};
%% Proxy app timed out.
-handle_info({timeout, Timer, Port}, S = #state{token = Tok})
- when Port == S#state.port, Timer == S#state.timer ->
- lager:info("~p: rpc request for ~s timed out, exiting", [self(), Tok]),
- p11p_manager:client_event(timeout, Tok),
- State = S#state{timer = undefined},
- {stop, normal, State};
+handle_info({timeout, Timer, Port}, State)
+ when Port == State#state.port, Timer == State#state.timer ->
+ lager:info("~p: rpc request for ~s timed out, exiting", [self(), State#state.token]),
+ p11p_manager:client_event(timeout, State#state.token),
+ NewState = State#state{timer = undefined},
+ {stop, normal, NewState};
handle_info(Info, State) ->
lager:debug("~p: Unhandled info: ~p~n", [self(), Info]),
@@ -176,7 +198,7 @@ do_send(Port, Buf) ->
end,
{ok, size(Buf)}.
-response_in(#state{replyto = Pid, timer = Timer, recv_count = Recv} = S,
+response_in(S = #state{replyto = Pid, timer = Timer, recv_count = Recv},
MsgIn, DataIn) ->
case p11p_rpc:parse(MsgIn, list_to_binary(DataIn)) of
{needmore, Msg} ->