Performs a step of SPNEGO auth for the HTTP transport and sets the username and kerberos_user_principal on 'connection_context' if auth is successful. 'header_token' is the value from an 'Authorization: Negotiate" header. Returns true if the step was successful and sets 'is_complete' to indicate if more steps are needed. Returns false if an error was encountered and the connection should be closed
| 879 | // if more steps are needed. Returns false if an error was encountered and the |
| 880 | // connection should be closed. |
| 881 | bool NegotiateAuth(ThriftServer::ConnectionContext* connection_context, |
| 882 | const AuthenticationHash& hash, const std::string& header_token, bool* is_complete) { |
| 883 | if (header_token.empty()) { |
| 884 | connection_context->return_headers.push_back("WWW-Authenticate: Negotiate"); |
| 885 | *is_complete = false; |
| 886 | return false; |
| 887 | } |
| 888 | std::string token; |
| 889 | // Note: according to RFC 2616, the correct format for the header is: |
| 890 | // 'Authorization: Negotiate <token>'. However, beeline incorrectly adds an additional |
| 891 | // ':', i.e. 'Authorization: Negotiate: <token>'. We handle that here. |
| 892 | TryStripPrefixString(header_token, ": ", &token); |
| 893 | string resp_token; |
| 894 | string username; |
| 895 | kudu::Status spnego_status = |
| 896 | kudu::gssapi::SpnegoStep(token, &resp_token, is_complete, &username); |
| 897 | if (spnego_status.ok()) { |
| 898 | if (!resp_token.empty()) { |
| 899 | string resp_header = Substitute("WWW-Authenticate: Negotiate $0", resp_token); |
| 900 | connection_context->return_headers.push_back(resp_header); |
| 901 | } |
| 902 | if (*is_complete) { |
| 903 | if (username.empty()) { |
| 904 | spnego_status = kudu::Status::RuntimeError( |
| 905 | "SPNEGO indicated complete, but got empty principal"); |
| 906 | // Crash in debug builds, but fall through to treating as an error in release. |
| 907 | LOG(DFATAL) << "Got no authenticated principal for SPNEGO-authenticated " |
| 908 | << " connection from " |
| 909 | << TNetworkAddressToString(connection_context->network_address) |
| 910 | << ": " << spnego_status.ToString(); |
| 911 | } else { |
| 912 | string short_user = GetShortUsernameFromKerberosPrincipal(username); |
| 913 | if (FLAGS_enable_ldap_auth && |
| 914 | FLAGS_enable_group_filter_check_for_authenticated_kerberos_user) { |
| 915 | |
| 916 | LOG(INFO) << "Checking LDAP group filters for " |
| 917 | << "username \"" << short_user << "\" " |
| 918 | << "parsed from user principal \"" |
| 919 | << username << "\"."; |
| 920 | |
| 921 | bool success = DoLdapCheckFilters(short_user.c_str()); |
| 922 | if (!success) { |
| 923 | LOG(WARNING) << "Got authenticated principal for SPNEGO-authenticated " |
| 924 | << "connection from " |
| 925 | << TNetworkAddressToString(connection_context->network_address) |
| 926 | << " but the authenticated user \"" << short_user << "\" " |
| 927 | << "didn't pass the group filters."; |
| 928 | return false; |
| 929 | } |
| 930 | } |
| 931 | |
| 932 | // Authentication was successful, so set the username on the connection. |
| 933 | connection_context->username = username; |
| 934 | // Save the username as Kerberos user principal in the connection context. |
| 935 | connection_context->kerberos_user_principal = username; |
| 936 | connection_context->kerberos_user_short = short_user; |
| 937 | // Create a cookie to return. |
| 938 | connection_context->return_headers.push_back( |
nothing calls this directly
no test coverage detected