pvAccessCPP  7.1.7-dev
security.h
1 /**
2  * Copyright - See the COPYRIGHT that is included with this distribution.
3  * pvAccessCPP is distributed subject to a Software License Agreement found
4  * in file LICENSE that is included with this distribution.
5  */
6 
7 #ifndef SECURITY_H
8 #define SECURITY_H
9 
10 /** @page pva_security PVA Security
11  *
12  * Summary of PVA auth. process.
13  *
14  @msc
15  arcgradient = 8;
16 
17  CP [label="Client Plugin"], CS [label="Client Session"], C [label="Client"], S [label="Server"], SS [label="Server Session"], SP [label="Server Plugin"];
18 
19  C:>S [label="Opens TCP connection"];
20  S box SP [label="Server lists plugins"];
21  S=>SP [label="isValidFor()"];
22  C<:S [label="CONNECTION_VALIDATION"];
23  CP box C [label="Client lists plugins"];
24  CP box C [label="Client select plugin"];
25  CP<=C [label="createSession()"];
26  CP>>C [label="Returns Session"];
27  CS<=C [label="initializationData()"];
28  CS>>C [label="Initial payload"];
29  C:>S [label="CONNECTION_VALIDATION"];
30  S=>SP [label="createSession()"];
31  S<<SP [label="Returns Session"];
32  --- [label="Optional (Repeatable)"];
33  S<=SS [label="sendSecurityPluginMessage()"];
34  C<:S [label="AUTHZ"];
35  CS<=C [label="messageReceived()"];
36  ...;
37  CS=>C [label="sendSecurityPluginMessage()"];
38  C:>S [label="AUTHZ"];
39  S=>SS [label="messageReceived()"];
40  ...;
41  --- [label="Completion"];
42  S<=SS [label="authenticationCompleted()"];
43  C<:S [label="CONNECTION_VALIDATED"];
44  CS<=C [label="authenticationComplete()"];
45  @endmsc
46  *
47  * Ownership
48  *
49  @dot
50  digraph authplugin {
51  External;
52  AuthenticationRegistry [shape=box];
53  AuthenticationPlugin [shape=box];
54  AuthenticationPluginControl [shape=box];
55  AuthenticationSession [shape=box];
56  External -> AuthenticationRegistry;
57  AuthenticationRegistry -> AuthenticationPlugin;
58  External -> AuthenticationSession;
59  AuthenticationSession -> AuthenticationPluginControl [color=green, style=dashed];
60  External -> AuthenticationPluginControl;
61  AuthenticationPluginControl -> AuthenticationSession;
62  AuthenticationPlugin -> AuthenticationSession [style=dashed];
63  }
64  @enddot
65  *
66  * Locking
67  *
68  * All methods of AuthenticationSession are called from a single thread.
69  * Methods of AuthenticationPlugin and AuthenticationPluginControl can be called
70  * from any threads.
71  *
72  * AuthenticationPluginControl is an Operation, AuthenticationSession is a Requester.
73  * @see provider_roles_requester_locking
74  */
75 
76 #ifdef epicsExportSharedSymbols
77 # define securityEpicsExportSharedSymbols
78 # undef epicsExportSharedSymbols
79 #endif
80 
81 #include <string>
82 #include <osiSock.h>
83 #include <epicsMutex.h>
84 
85 #include <pv/status.h>
86 #include <pv/pvData.h>
87 #include <pv/sharedPtr.h>
88 
89 #ifdef securityEpicsExportSharedSymbols
90 # define epicsExportSharedSymbols
91 # undef securityEpicsExportSharedSymbols
92 #endif
93 
94 #include <pv/pvaDefs.h>
95 #include <pv/pvaConstants.h>
96 #include <pv/serializationHelper.h>
97 #include <pv/logger.h>
98 
99 #include <shareLib.h>
100 
101 namespace epics {
102 namespace pvAccess {
103 
104 /** @brief Information provded by a client to a server-type ChannelProvider.
105  *
106  * All peers must be identified by a peer name, which may be a network address (IP+port#) and transport.
107  * Peer names must be unique for a given transport.
108  *
109  * Transport names include:
110  *
111  * # "local" in-process client. Peer name is optional and arbitrary. Must set local flag.
112  * # "pva" PVA over TCP. Used by PVA client provider. Peer name is IP address and TCP port number as "XXX.XXX.XXX.XXX:YYYYY".
113  *
114  * Authority names include:
115  *
116  * "anonymous" - No credentials provided. Must not set identified flag.
117  * "plain" - Unauthenticated credential.
118  */
119 struct epicsShareClass PeerInfo {
120  POINTER_DEFINITIONS(PeerInfo);
121 
122  static size_t num_instances;
123 
124  std::string peer; //!< network address of remote peer. eg. "192.168.1.1:5075".
125  std::string transport; //!< transport protocol used eg. "pva". Must not be empty.
126  std::string authority; //!< authentication mechanism used. eg. "anonymous" or "gssapi". Must not be empty.
127  std::string realm; //!< scope of authority. eg. "mylab.gov"
128  std::string account; //!< aka. user name
129 
130  //! NULL or extra authority specific information.
131  pvData::PVStructure::const_shared_pointer aux;
132 
133  typedef std::set<std::string> roles_t;
134  //! Set of strings which may be used to modify access control decisions.
135  roles_t roles;
136 
137  unsigned transportVersion; //!< If applicable, the protocol minor version number
138 
139  // attributes for programatic consumption
140  bool local; //!< Short-hand for transport=="local"
141  bool identified; //!< Short-hand for authority!="anonymous"
142 
143  PeerInfo();
144  virtual ~PeerInfo();
145 };
146 
147 /** A particular authentication exchange. See AuthenticationPlugin::createSession()
148  *
149  * @note Must not hold a strong reference to AuthenticationPluginControl
150  */
151 class epicsShareClass AuthenticationSession
152 {
153 public:
154  POINTER_DEFINITIONS(AuthenticationSession);
155 
156  virtual ~AuthenticationSession();
157 
158  //! For client plugins only, call to find the payload returned with CONNECTION_VALIDATION.
159  //! May return NULL.
160  virtual epics::pvData::PVStructure::const_shared_pointer initializationData()
161  { return epics::pvData::PVStructure::const_shared_pointer(); }
162 
163  //! Called when an AUTHZ message is recieved from the peer.
164  //! See AuthenticationPluginControl::sendSecurityPluginMessage().
165  //! callee accepts ownership of data, which will not be modified.
166  virtual void messageReceived(epics::pvData::PVStructure::const_shared_pointer const & data) {}
167 
168  /** For client plugins only. Notification that server has declared the exchange complete.
169  * @param status Check Status::isSuccess()
170  * @param peer Final information about pe
171  */
172  virtual void authenticationComplete(const epics::pvData::Status& status) {}
173 };
174 
175 //! Callbacks for use by AuthenticationSession
176 class epicsShareClass AuthenticationPluginControl
177 {
178 public:
179  POINTER_DEFINITIONS(AuthenticationPluginControl);
180  virtual ~AuthenticationPluginControl();
181 
182  //! Send AUTHZ to peer with payload.
183  //! caller gives up ownership of data, which must not be modified.
184  virtual void sendSecurityPluginMessage(epics::pvData::PVStructure::const_shared_pointer const & data) = 0;
185 
186  /** Called by server plugin to indicate the the exchange has completed.
187  *
188  * @param status If !status.isSuccess() then the connection will be closed without being used.
189  * @param peer Partially initialized PeerInfo. See AuthenticationPlugin::createSession().
190  * PeerInfo::realm and/or PeerInfo::account will now be considered valid.
191  * Caller transfers ownership to callee, which may modify.
192  */
193  virtual void authenticationCompleted(const epics::pvData::Status& status,
194  const std::tr1::shared_ptr<PeerInfo>& peer) = 0;
195 };
196 
197 //! Actor through which authentication exchanges are initiated.
198 class epicsShareClass AuthenticationPlugin
199 {
200 public:
201  POINTER_DEFINITIONS(AuthenticationPlugin);
202  virtual ~AuthenticationPlugin();
203 
204  /** Allow this plugin to be advertised to a particular peer.
205  *
206  * At this point the PeerInfo has only been partially initialized with
207  * transport/protocol specific information: PeerInfo::peer, PeerInfo::transport, and PeerInfo::transportVersion.
208  */
209  virtual bool isValidFor(const PeerInfo& peer) const { return true; }
210 
211  /** Begin a new session with a peer.
212  *
213  * @param peer Partially initialized PeerInfo. See isValidFor().
214  * PeerInfo::authority is also set.
215  * Caller transfers ownership to callee, which may modify.
216  * @param control callee uses to asynchronously continue, and complete the session.
217  * @param data Always NULL for client-type plugins. For server-type plugins,
218  * the result of initializationData() from the peer
219  */
220  virtual std::tr1::shared_ptr<AuthenticationSession> createSession(
221  const std::tr1::shared_ptr<PeerInfo>& peer,
222  std::tr1::shared_ptr<AuthenticationPluginControl> const & control,
223  epics::pvData::PVStructure::shared_pointer const & data) = 0;
224 };
225 
226 /** Registry(s) for plugins
227  */
228 class epicsShareClass AuthenticationRegistry
229 {
230  EPICS_NOT_COPYABLE(AuthenticationRegistry) // would need locking
231 public:
232  POINTER_DEFINITIONS(AuthenticationRegistry);
233 
234 private:
235  typedef std::map<int, std::pair<std::string, AuthenticationPlugin::shared_pointer> > map_t;
236  map_t map;
237  mutable epicsMutex mutex;
238 public:
239  typedef std::vector<map_t::mapped_type> list_t;
240 
241  //! The client side of the conversation
242  static AuthenticationRegistry& clients();
243  //! The server side of the conversation
244  static AuthenticationRegistry& servers();
245 
246  AuthenticationRegistry() {}
247  ~AuthenticationRegistry();
248 
249  //! Save a copy of the current registry in order of increasing priority
250  void snapshot(list_t& plugmap) const;
251 
252  /** @brief Add a new plugin to this registry.
253  *
254  @param prio Order in which plugins are considered. highest is preferred.
255  @param name Name under which this plugin will be known
256  @param plugin Plugin instance
257  */
258  void add(int prio, const std::string& name, const AuthenticationPlugin::shared_pointer& plugin);
259  //! Remove an existing entry. Remove true if the entry was actually removed.
260  bool remove(const AuthenticationPlugin::shared_pointer& plugin);
261  //! Fetch a single plugin explicitly by name.
262  //! @returns NULL if no entry for this name is available.
263  AuthenticationPlugin::shared_pointer lookup(const std::string& name) const;
264 };
265 
266 //! I modify PeerInfo after authentication is complete.
267 //! Usually to update PeerInfo::roles
268 class epicsShareClass AuthorizationPlugin
269 {
270 public:
271  POINTER_DEFINITIONS(AuthorizationPlugin);
272 
273  virtual ~AuthorizationPlugin();
274 
275  //! Hook to modify PeerInfo
276  virtual void authorize(const std::tr1::shared_ptr<PeerInfo>& peer) =0;
277 };
278 
279 class epicsShareClass AuthorizationRegistry
280 {
281  EPICS_NOT_COPYABLE(AuthorizationRegistry)
282 public:
283  POINTER_DEFINITIONS(AuthenticationRegistry);
284 
285  static AuthorizationRegistry &plugins();
286 
287  AuthorizationRegistry();
288  ~AuthorizationRegistry();
289 
290 private:
291  typedef std::map<int, AuthorizationPlugin::shared_pointer> map_t;
292  map_t map;
293  size_t busy;
294  mutable epicsMutex mutex;
295 public:
296 
297  void add(int prio, const AuthorizationPlugin::shared_pointer& plugin);
298  bool remove(const AuthorizationPlugin::shared_pointer& plugin);
299  void run(const std::tr1::shared_ptr<PeerInfo>& peer);
300 };
301 
302 /** @brief Query OS specific DB for role/group names assocated with a user account.
303  * @param account User name
304  * @param roles Role names are added to this set. Existing names are not removed.
305  */
306 epicsShareFunc
307 void osdGetRoles(const std::string &account, PeerInfo::roles_t& roles);
308 
309 }
310 }
311 
312 #endif // SECURITY_H