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