xref: /aosp_15_r20/external/cronet/net/http/http_auth_gssapi_posix.h (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2012 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #ifndef NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
6 #define NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
7 
8 #include <string>
9 #include <string_view>
10 
11 #include "base/gtest_prod_util.h"
12 #include "base/memory/raw_ptr.h"
13 #include "base/native_library.h"
14 #include "base/values.h"
15 #include "build/build_config.h"
16 #include "net/base/completion_once_callback.h"
17 #include "net/base/net_export.h"
18 #include "net/http/http_auth.h"
19 #include "net/http/http_auth_mechanism.h"
20 
21 #if BUILDFLAG(IS_APPLE)
22 #include <GSS/gssapi.h>
23 #elif BUILDFLAG(IS_FREEBSD)
24 #include <gssapi/gssapi.h>
25 #else
26 #include <gssapi.h>
27 #endif
28 
29 namespace net {
30 
31 class HttpAuthChallengeTokenizer;
32 
33 // Mechanism OID for GSSAPI. We always use SPNEGO.
34 NET_EXPORT_PRIVATE extern gss_OID CHROME_GSS_SPNEGO_MECH_OID_DESC;
35 
36 // GSSAPILibrary is introduced so unit tests can mock the calls to the GSSAPI
37 // library. The default implementation attempts to load one of the standard
38 // GSSAPI library implementations, then simply passes the arguments on to
39 // that implementation.
40 class NET_EXPORT_PRIVATE GSSAPILibrary {
41  public:
42   virtual ~GSSAPILibrary() = default;
43 
44   // Initializes the library, including any necessary dynamic libraries.
45   // This is done separately from construction (which happens at startup time)
46   // in order to delay work until the class is actually needed.
47   virtual bool Init(const NetLogWithSource& net_log) = 0;
48 
49   // These methods match the ones in the GSSAPI library.
50   virtual OM_uint32 import_name(
51       OM_uint32* minor_status,
52       const gss_buffer_t input_name_buffer,
53       const gss_OID input_name_type,
54       gss_name_t* output_name) = 0;
55   virtual OM_uint32 release_name(
56       OM_uint32* minor_status,
57       gss_name_t* input_name) = 0;
58   virtual OM_uint32 release_buffer(
59       OM_uint32* minor_status,
60       gss_buffer_t buffer) = 0;
61   virtual OM_uint32 display_name(
62       OM_uint32* minor_status,
63       const gss_name_t input_name,
64       gss_buffer_t output_name_buffer,
65       gss_OID* output_name_type) = 0;
66   virtual OM_uint32 display_status(
67       OM_uint32* minor_status,
68       OM_uint32 status_value,
69       int status_type,
70       const gss_OID mech_type,
71       OM_uint32* message_contex,
72       gss_buffer_t status_string) = 0;
73   virtual OM_uint32 init_sec_context(
74       OM_uint32* minor_status,
75       const gss_cred_id_t initiator_cred_handle,
76       gss_ctx_id_t* context_handle,
77       const gss_name_t target_name,
78       const gss_OID mech_type,
79       OM_uint32 req_flags,
80       OM_uint32 time_req,
81       const gss_channel_bindings_t input_chan_bindings,
82       const gss_buffer_t input_token,
83       gss_OID* actual_mech_type,
84       gss_buffer_t output_token,
85       OM_uint32* ret_flags,
86       OM_uint32* time_rec) = 0;
87   virtual OM_uint32 wrap_size_limit(
88       OM_uint32* minor_status,
89       const gss_ctx_id_t context_handle,
90       int conf_req_flag,
91       gss_qop_t qop_req,
92       OM_uint32 req_output_size,
93       OM_uint32* max_input_size) = 0;
94   virtual OM_uint32 delete_sec_context(
95       OM_uint32* minor_status,
96       gss_ctx_id_t* context_handle,
97       gss_buffer_t output_token) = 0;
98   virtual OM_uint32 inquire_context(
99       OM_uint32* minor_status,
100       const gss_ctx_id_t context_handle,
101       gss_name_t* src_name,
102       gss_name_t* targ_name,
103       OM_uint32* lifetime_rec,
104       gss_OID* mech_type,
105       OM_uint32* ctx_flags,
106       int* locally_initiated,
107       int* open) = 0;
108   virtual const std::string& GetLibraryNameForTesting() = 0;
109 };
110 
111 // GSSAPISharedLibrary class is defined here so that unit tests can access it.
112 class NET_EXPORT_PRIVATE GSSAPISharedLibrary : public GSSAPILibrary {
113  public:
114   // If |gssapi_library_name| is empty, hard-coded default library names are
115   // used.
116   explicit GSSAPISharedLibrary(const std::string& gssapi_library_name);
117   ~GSSAPISharedLibrary() override;
118 
119   // GSSAPILibrary methods:
120   bool Init(const NetLogWithSource& net_log) override;
121   OM_uint32 import_name(OM_uint32* minor_status,
122                         const gss_buffer_t input_name_buffer,
123                         const gss_OID input_name_type,
124                         gss_name_t* output_name) override;
125   OM_uint32 release_name(OM_uint32* minor_status,
126                          gss_name_t* input_name) override;
127   OM_uint32 release_buffer(OM_uint32* minor_status,
128                            gss_buffer_t buffer) override;
129   OM_uint32 display_name(OM_uint32* minor_status,
130                          const gss_name_t input_name,
131                          gss_buffer_t output_name_buffer,
132                          gss_OID* output_name_type) override;
133   OM_uint32 display_status(OM_uint32* minor_status,
134                            OM_uint32 status_value,
135                            int status_type,
136                            const gss_OID mech_type,
137                            OM_uint32* message_contex,
138                            gss_buffer_t status_string) override;
139   OM_uint32 init_sec_context(OM_uint32* minor_status,
140                              const gss_cred_id_t initiator_cred_handle,
141                              gss_ctx_id_t* context_handle,
142                              const gss_name_t target_name,
143                              const gss_OID mech_type,
144                              OM_uint32 req_flags,
145                              OM_uint32 time_req,
146                              const gss_channel_bindings_t input_chan_bindings,
147                              const gss_buffer_t input_token,
148                              gss_OID* actual_mech_type,
149                              gss_buffer_t output_token,
150                              OM_uint32* ret_flags,
151                              OM_uint32* time_rec) override;
152   OM_uint32 wrap_size_limit(OM_uint32* minor_status,
153                             const gss_ctx_id_t context_handle,
154                             int conf_req_flag,
155                             gss_qop_t qop_req,
156                             OM_uint32 req_output_size,
157                             OM_uint32* max_input_size) override;
158   OM_uint32 delete_sec_context(OM_uint32* minor_status,
159                                gss_ctx_id_t* context_handle,
160                                gss_buffer_t output_token) override;
161   OM_uint32 inquire_context(OM_uint32* minor_status,
162                             const gss_ctx_id_t context_handle,
163                             gss_name_t* src_name,
164                             gss_name_t* targ_name,
165                             OM_uint32* lifetime_rec,
166                             gss_OID* mech_type,
167                             OM_uint32* ctx_flags,
168                             int* locally_initiated,
169                             int* open) override;
170   const std::string& GetLibraryNameForTesting() override;
171 
172  private:
173   FRIEND_TEST_ALL_PREFIXES(HttpAuthGSSAPIPOSIXTest, GSSAPIStartup);
174 
175   bool InitImpl(const NetLogWithSource& net_log);
176   // Finds a usable dynamic library for GSSAPI and loads it.  The criteria are:
177   //   1. The library must exist.
178   //   2. The library must export the functions we need.
179   base::NativeLibrary LoadSharedLibrary(const NetLogWithSource& net_log);
180   bool BindMethods(base::NativeLibrary lib,
181                    std::string_view library_name,
182                    const NetLogWithSource& net_log);
183 
184   bool initialized_ = false;
185 
186   std::string gssapi_library_name_;
187   // Need some way to invalidate the library.
188   base::NativeLibrary gssapi_library_ = nullptr;
189 
190   // Function pointers
191   decltype(&gss_import_name) import_name_ = nullptr;
192   decltype(&gss_release_name) release_name_ = nullptr;
193   decltype(&gss_release_buffer) release_buffer_ = nullptr;
194   decltype(&gss_display_name) display_name_ = nullptr;
195   decltype(&gss_display_status) display_status_ = nullptr;
196   decltype(&gss_init_sec_context) init_sec_context_ = nullptr;
197   decltype(&gss_wrap_size_limit) wrap_size_limit_ = nullptr;
198   decltype(&gss_delete_sec_context) delete_sec_context_ = nullptr;
199   decltype(&gss_inquire_context) inquire_context_ = nullptr;
200 };
201 
202 // ScopedSecurityContext releases a gss_ctx_id_t when it goes out of
203 // scope.
204 class ScopedSecurityContext {
205  public:
206   explicit ScopedSecurityContext(GSSAPILibrary* gssapi_lib);
207 
208   ScopedSecurityContext(const ScopedSecurityContext&) = delete;
209   ScopedSecurityContext& operator=(const ScopedSecurityContext&) = delete;
210 
211   ~ScopedSecurityContext();
212 
get()213   gss_ctx_id_t get() const { return security_context_; }
receive()214   gss_ctx_id_t* receive() { return &security_context_; }
215 
216  private:
217   gss_ctx_id_t security_context_ = GSS_C_NO_CONTEXT;
218   raw_ptr<GSSAPILibrary> gssapi_lib_;
219 };
220 
221 
222 // TODO(ahendrickson): Share code with HttpAuthSSPI.
223 class NET_EXPORT_PRIVATE HttpAuthGSSAPI : public HttpAuthMechanism {
224  public:
225   HttpAuthGSSAPI(GSSAPILibrary* library,
226                  const gss_OID gss_oid);
227   ~HttpAuthGSSAPI() override;
228 
229   // HttpAuthMechanism implementation:
230   bool Init(const NetLogWithSource& net_log) override;
231   bool NeedsIdentity() const override;
232   bool AllowsExplicitCredentials() const override;
233   HttpAuth::AuthorizationResult ParseChallenge(
234       HttpAuthChallengeTokenizer* tok) override;
235   int GenerateAuthToken(const AuthCredentials* credentials,
236                         const std::string& spn,
237                         const std::string& channel_bindings,
238                         std::string* auth_token,
239                         const NetLogWithSource& net_log,
240                         CompletionOnceCallback callback) override;
241   void SetDelegation(HttpAuth::DelegationType delegation_type) override;
242 
243  private:
244   int GetNextSecurityToken(const std::string& spn,
245                            const std::string& channel_bindings,
246                            gss_buffer_t in_token,
247                            gss_buffer_t out_token,
248                            const NetLogWithSource& net_log);
249 
250   gss_OID gss_oid_;
251   raw_ptr<GSSAPILibrary> library_;
252   std::string decoded_server_auth_token_;
253   ScopedSecurityContext scoped_sec_context_;
254   HttpAuth::DelegationType delegation_type_ = HttpAuth::DelegationType::kNone;
255 };
256 
257 // Diagnostics
258 
259 // GetGssStatusCodeValue constructs a base::Value::Dict containing a status code
260 // and a message.
261 //
262 //     {
263 //       "status" : <status value as a number>,
264 //       "message": [
265 //          <list of strings explaining what that number means>
266 //       ]
267 //     }
268 //
269 // Messages are looked up via gss_display_status() exposed by |gssapi_lib|. The
270 // type of status code should be indicated by setting |status_code_type| to
271 // either |GSS_C_MECH_CODE| or |GSS_C_GSS_CODE|.
272 //
273 // Mechanism specific codes aren't unique, so the mechanism needs to be
274 // identified to look up messages if |status_code_type| is |GSS_C_MECH_CODE|.
275 // Since no mechanism OIDs are passed in, mechanism specific status codes will
276 // likely not have messages.
277 NET_EXPORT_PRIVATE base::Value::Dict GetGssStatusCodeValue(
278     GSSAPILibrary* gssapi_lib,
279     OM_uint32 status,
280     OM_uint32 status_code_type);
281 
282 // Given major and minor GSSAPI status codes, returns a base::Value::Dict
283 // encapsulating the codes as well as their meanings as expanded via
284 // gss_display_status().
285 //
286 // The base::Value::Dict has the following structure:
287 //   {
288 //     "function": <name of GSSAPI function that returned the error>
289 //     "major_status": {
290 //       "status" : <status value as a number>,
291 //       "message": [
292 //          <list of strings hopefully explaining what that number means>
293 //       ]
294 //     },
295 //     "minor_status": {
296 //       "status" : <status value as a number>,
297 //       "message": [
298 //          <list of strings hopefully explaining what that number means>
299 //       ]
300 //     }
301 //   }
302 //
303 // Passing nullptr to |gssapi_lib| will skip the message lookups. Thus the
304 // returned value will be missing the "message" fields. The same is true if the
305 // message lookup failed for some reason, or if the lookups succeeded but
306 // yielded an empty message.
307 NET_EXPORT_PRIVATE base::Value::Dict GetGssStatusValue(
308     GSSAPILibrary* gssapi_lib,
309     std::string_view method,
310     OM_uint32 major_status,
311     OM_uint32 minor_status);
312 
313 // OidToValue returns a base::Value::Dict representing an OID. The structure of
314 // the value is:
315 //   {
316 //     "oid":    <symbolic name of OID if it is known>
317 //     "length": <length in bytes of serialized OID>,
318 //     "bytes":  <hexdump of up to 1024 bytes of serialized OID>
319 //   }
320 NET_EXPORT_PRIVATE base::Value::Dict OidToValue(const gss_OID oid);
321 
322 // GetDisplayNameValue returns a base::Value::Dict representing a gss_name_t. It
323 // invokes |gss_display_name()| via |gssapi_lib| to determine the display name
324 // associated with |gss_name|.
325 //
326 // The structure of the returned value is:
327 //   {
328 //     "gss_name": <display name as returned by gss_display_name()>,
329 //     "type": <OID indicating type. See OidToValue() for structure of this
330 //              field>
331 //   }
332 //
333 // If the lookup failed, then the structure is:
334 //   {
335 //     "error": <error. See GetGssStatusValue() for structure.>
336 //   }
337 //
338 // Note that |gss_name_t| is platform dependent. If |gss_display_name| fails,
339 // there's no good value to display in its stead.
340 NET_EXPORT_PRIVATE base::Value::Dict GetDisplayNameValue(
341     GSSAPILibrary* gssapi_lib,
342     const gss_name_t gss_name);
343 
344 // GetContextStateAsValue returns a base::Value::Dict that describes the state
345 // of a GSSAPI context. The structure of the value is:
346 //
347 //   {
348 //     "source": {
349 //       "name": <GSSAPI principal name of source (e.g. the user)>,
350 //       "type": <OID of name type>
351 //     },
352 //     "target": {
353 //       "name": <GSSAPI principal name of target (e.g. the server)>,
354 //       "type": <OID of name type>
355 //     },
356 //     "lifetime": <Lifetime of the negotiated context in seconds.>,
357 //     "mechanism": <OID of negotiated mechanism>,
358 //     "flags": <Context flags. See documentation for gss_inquire_context for
359 //               flag values>
360 //     "open": <True if the context has finished the handshake>
361 //   }
362 //
363 // If the inquiry fails, the following is returned:
364 //   {
365 //     "error": <error. See GetGssStatusValue() for structure.>
366 //   }
367 NET_EXPORT_PRIVATE base::Value::Dict GetContextStateAsValue(
368     GSSAPILibrary* gssapi_lib,
369     const gss_ctx_id_t context_handle);
370 }  // namespace net
371 
372 #endif  // NET_HTTP_HTTP_AUTH_GSSAPI_POSIX_H_
373