Http_CallbackResult IsClientAuthorized()

in Unix/http/httpauth.c [1712:2247]


Http_CallbackResult IsClientAuthorized(_In_ Http_SR_SocketData * handler)
{
    Http_CallbackResult authorised = PRT_RETURN_FALSE;
    HttpHeaders *headers = &handler->recvHeaders;

    static const char RESPONSE_HEADER_AUTHORIZED[] = "HTTP/1.1 200 Success\r\n" "Content-Length: 0\r\n" "\r\n";
    static const int  RESPONSE_HEADER_AUTHORIZED_LEN = MI_COUNT(RESPONSE_HEADER_AUTHORIZED)-1;
#if AUTHORIZATION
    static const char RESPONSE_HEADER_BAD_REQUEST[] = "HTTP/1.1 400 Bad Request\r\n" "Content-Length: 0\r\n" "\r\n";
    static const int  RESPONSE_HEADER_BAD_REQUEST_LEN = MI_COUNT(RESPONSE_HEADER_BAD_REQUEST)-1;
    OM_uint32 flags = 0;
    const char *protocol_p = NULL;
#endif

    char *auth_response = NULL;
    int response_len = 0;

    if (IsAuthCallsIgnored())
    {
        handler->httpErrorCode = 0; // We let the transaction set the error code
        handler->isAuthorised = TRUE;
        return PRT_RETURN_TRUE;
    }

    if (!headers)
    {
        handler->httpErrorCode = HTTP_ERROR_CODE_INTERNAL_SERVER_ERROR;
        goto Done;
    }

    if (Strncasecmp(headers->authorization, AUTHENTICATION_BASIC, AUTHENTICATION_BASIC_LENGTH) == 0)
    {
        handler->httpAuthType = AUTH_METHOD_BASIC;
        if (!headers->username || !headers->password)
        {
            handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
            auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
            response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

            trace_HTTP_UserAuthFailed("missing user name or password");
            handler->authFailed = TRUE;

            _SendAuthResponse(handler, auth_response, response_len);
            return PRT_RETURN_FALSE;
        }

        if (0 == IsRoot())
        {
            if (0 != AuthenticateUser(headers->username, headers->password))
            {
                handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
                auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
                response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

                trace_HTTP_UserAuthFailed("user not authenticated");
                handler->authFailed = TRUE;
                
                _SendAuthResponse(handler, auth_response, response_len);
                return PRT_RETURN_FALSE;
            }
        }
        else
        {
            /* Verify if user is in cache already */
            if (0 != CredCache_CheckUser(headers->username, headers->password))
            {
                trace_AskServerToAuthenticate();

                // Stop selector processing of this socket
                handler->handler.mask &= ~SELECTOR_READ;

                if (0 != AskServerToAuthenticate(headers->username, 
                                                 headers->password, 
                                                 (MI_Uint64)(uintptr_t)handler,
                                                 _ServerAuthenticateCallback))
                {
                    handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
                    auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
                    response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

                    trace_HTTP_UserAuthFailed("failed to ask server to authenticate");
                    handler->authFailed = TRUE;

                    _SendAuthResponse(handler, auth_response, response_len);
                    return PRT_RETURN_FALSE;
                }
                return PRT_CONTINUE;
            }
        }

        if (0 != LookupUser(headers->username, &handler->authInfo.uid, &handler->authInfo.gid))
        {
            trace_GetUserUidGid_Failed(headers->username);

            handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
            auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
            response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

            trace_HTTP_UserAuthFailed("basic auth user creds not present");
            handler->authFailed = TRUE;
            _SendAuthResponse(handler, auth_response, response_len);
            return PRT_RETURN_FALSE;
        }

        if (1 != IsUserAuthorized(headers->username, handler->authInfo.gid))
        {
            trace_Authorization_Failed(headers->username);
        
            handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
            auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
            response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

            trace_HTTP_UserAuthFailed("basic auth user authorization failed");
            handler->authFailed = TRUE;
            _SendAuthResponse(handler, auth_response, response_len);
            goto Done;
        }

        handler->httpErrorCode = 0; // Let the request do the error code
        handler->isAuthorised = TRUE;
        return PRT_RETURN_TRUE;
    }
#ifdef AUTHORIZATION
    else
    {
        const gss_OID_desc mechset_avail_elems[] = {
            { 6, "\053\006\001\005\005\002" },                  // Spnego
            { 10, "\x2b\x06\x01\x04\x01\x82\x37\x02\x02\x0a" }, // ntlm
            { 9, "\052\206\110\206\367\022\001\002\002" },      // krb5
            { 6, "\053\006\001\005\002\005" } // mech_iakerb
        };
        const gss_OID_set_desc mechset_avail = { 4, (gss_OID) mechset_avail_elems };

        const gss_OID_desc mechset_krb5_elems[] = {
            { 9, "\052\206\110\206\367\022\001\002\002" },      // krb5
            { 6, "\053\006\001\005\002\005" }                   // mech_iakerb
        };

        const gss_OID_set_desc mechset_krb5 = { 2, (gss_OID) mechset_krb5_elems };

        OM_uint32 maj_stat, min_stat;
        gss_ctx_id_t context_hdl = GSS_C_NO_CONTEXT;
        gss_buffer_desc input_token, output_token;
        gss_OID_set mechset = NULL;

        // Ensure the GSS lib is loaded

        if (!Once_Invoke(&g_once_state, _GssInitLibrary, NULL))
        {
            trace_HTTP_LoadGssFailed("");
            return PRT_RETURN_FALSE;
        }

        if (handler->pAuthContext)
        {
            context_hdl = (gss_ctx_id_t) handler->pAuthContext;
        }

        if (Strncasecmp(headers->authorization, AUTHENTICATION_NEGOTIATE, AUTHENTICATION_NEGOTIATE_LENGTH) == 0)
        {
            // OM_uint32 flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG;
            // gss_OID mech_type;

            protocol_p = AUTHENTICATION_NEGOTIATE;
            handler->httpAuthType = AUTH_METHOD_NEGOTIATE;
            mechset = (gss_OID_set) & mechset_avail;

        }
        else if (Strncasecmp(headers->authorization, AUTHENTICATION_KERBEROS, AUTHENTICATION_KERBEROS_LENGTH) == 0)
        {
            // OM_uint32 flags = GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG | GSS_C_MUTUAL_FLAG;
            // gss_OID mech_type;

            protocol_p = AUTHENTICATION_KERBEROS;
            handler->httpAuthType = AUTH_METHOD_KERBEROS;
            mechset = (gss_OID_set) & mechset_krb5;
        }
        else
        {
            trace_Wsman_UnsupportedAuthentication(headers->authorization);
            handler->httpErrorCode = HTTP_ERROR_CODE_INTERNAL_SERVER_ERROR;
            auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
            response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

            handler->authFailed = TRUE;

            _SendAuthResponse(handler, auth_response, response_len);
            return PRT_RETURN_FALSE;
        }

        if (_getInputToken(handler, headers->authorization, &input_token) != 0)
        {
            trace_HTTP_InvalidAuthToken();
            handler->httpErrorCode = HTTP_ERROR_CODE_INTERNAL_SERVER_ERROR;
            auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
            response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

            handler->authFailed = TRUE;

            _SendAuthResponse(handler, auth_response, response_len);
            return PRT_RETURN_FALSE;
        }

        if (handler->httpErrorCode == 0)
        {
            gss_cred_id_t verifier_cred_handle = GSS_C_NO_CREDENTIAL;

            /* Get acceptor cred for principal. */
            maj_stat = (*_g_gssState.Gss_Acquire_Cred)(&min_stat, GSS_C_NO_NAME, GSS_C_INDEFINITE, mechset, GSS_C_ACCEPT, &verifier_cred_handle, NULL, NULL); // Name needs to not be null?
            if (_check_gsserr("gss_acquire_cred(acceptor) ", maj_stat, min_stat))
            {
                handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
                auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
                response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

                handler->authFailed = TRUE;

                _SendAuthResponse(handler, auth_response, response_len);
                return PRT_RETURN_FALSE;
            }
            else 
            {
                handler->pVerifierCred = (void*)verifier_cred_handle;
            }   
        }
        // (void)DecodeToken(&input_token);
        maj_stat = (*_g_gssState.Gss_Accept_Sec_Context)(&min_stat,    // ok
                                          &context_hdl, // ok
                                          (gss_cred_id_t)handler->pVerifierCred,  // acceptor_cred_handle
                                          &input_token, // Base64 decoded the SPNEGO token
                                          GSS_C_NO_CHANNEL_BINDINGS,    //input_channel_bindings (more security?)
                                          NULL, // client_name / src_name
                                          NULL, // mech_type optional Security mechanism used
                                          &output_token,    // ok
                                          &flags,   // flags are retained in the handler for the use of gss_wrap and gss_unwrap
                                          NULL, // time_rec number of seconds for which the context will remain valid
                                          NULL);    // deleg_cred

        handler->pAuthContext = context_hdl;

        PAL_Free(input_token.value);

        if (maj_stat == GSS_S_COMPLETE)
        {
            /* We are authenticated, now need to be authorised */

            trace_HTTP_AuthComplete();
            traceSupplementaryInfo(maj_stat);

            gss_buffer_t user_name = _getPrincipalName(context_hdl);
#define MAX_HOSTNAME_LEN 256
            static char hostname[MAX_HOSTNAME_LEN] = { 0 };
            char *username = (char *)user_name->value;

            int ret = gethostname(hostname, MAX_HOSTNAME_LEN);
            if (ret == 0)
            {
                if (Strncasecmp(hostname, (char *)username, strlen(hostname)) == 0)
                {

                    // If the domain is this machine, we can strip the domain name and do a local lookup

                    char *p = memchr(user_name->value, '\\',
                                     user_name->length);
                    if (p)
                    {
                        username = ++p;
                    }
                }
            }

            if (0 != LookupUser(username, &handler->authInfo.uid, &handler->authInfo.gid))
            {

                // After all that, it would be weird for this to fail, but it is possible
                // on a misconfigured system. either way, if its not there its not there.

                trace_GetUserUidGid_Failed(headers->username);

                handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
                handler->authFailed = TRUE;
                auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
                response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

                _SendAuthResponse(handler, auth_response, response_len);

                (* _g_gssState.Gss_Delete_Sec_Context)(&min_stat, &context_hdl, NULL);

                handler->pAuthContext = NULL;
                handler->authFailed   = TRUE;

                if (handler->pVerifierCred)
                {    
                    (* _g_gssState.Gss_Release_Cred)(&min_stat, handler->pVerifierCred);
                    handler->pVerifierCred = NULL;
                }

                (* _g_gssState.Gss_Release_Buffer)(&min_stat, user_name);
                PAL_Free(user_name);

                goto Done;
            }
            else 
            {
                if (1 != IsUserAuthorized(username, handler->authInfo.gid))
                {
                    trace_Authorization_Failed(username);
                    
                    handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
                    auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
                    response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

                    trace_HTTP_UserAuthFailed("NTLM/Kerberos user authorization failed");
                    handler->authFailed = TRUE;
                    _SendAuthResponse(handler, auth_response, response_len);

                    (* _g_gssState.Gss_Delete_Sec_Context)(&min_stat, &context_hdl, NULL);

                    handler->pAuthContext = NULL;

                    if (handler->pVerifierCred)
                    {    
                        (* _g_gssState.Gss_Release_Cred)(&min_stat, handler->pVerifierCred);
                        handler->pVerifierCred = NULL;
                    }

                    (* _g_gssState.Gss_Release_Buffer)(&min_stat, user_name);
                    PAL_Free(user_name);

                    goto Done;
                }
                
                (* _g_gssState.Gss_Release_Buffer)(&min_stat, user_name);
                handler->negFlags = flags;

                PAL_Free(user_name);

                handler->isAuthorised = TRUE;
                if (headers->contentLength == 0)
                {
                    // Apparently we were just authorising the connection so far and the request is 
                    //  yet to come. Treat this in the same way as a continue except succeed
                    
                    handler->httpErrorCode = HTTP_ERROR_CODE_OK;
                    if (output_token.length != 0)
                    {
                        auth_response = _BuildAuthResponse(protocol_p, handler->httpErrorCode, &output_token, &response_len);
                        if (auth_response == NULL)
                        {
                            trace_HTTP_CannotBuildAuthResponse();
                            handler->httpErrorCode = HTTP_ERROR_CODE_INTERNAL_SERVER_ERROR;
                        }
                        (*_g_gssState.Gss_Release_Buffer)(&min_stat, &output_token);
        
                        _SendAuthResponse(handler, auth_response, response_len);
                        PAL_Free(auth_response);
                    }
                    else 
                    {
                        // If we are doing a key exchange we send our key back after success

                        if (output_token.length > 0 )
                        {
                            auth_response = _BuildAuthResponse(protocol_p, handler->httpErrorCode, &output_token, &response_len);
                            if (auth_response == NULL)
                            {
                                trace_HTTP_CannotBuildAuthResponse();
                                handler->httpErrorCode = HTTP_ERROR_CODE_INTERNAL_SERVER_ERROR;
                            }
                            (*_g_gssState.Gss_Release_Buffer)(&min_stat, &output_token);
                            _SendAuthResponse(handler, auth_response, response_len);
                            PAL_Free(auth_response);
                        }
                        else 
                        {
                            auth_response = (char *)RESPONSE_HEADER_AUTHORIZED;
                            response_len  = RESPONSE_HEADER_AUTHORIZED_LEN;
                            _SendAuthResponse(handler, auth_response, response_len);
                        }
                    }
                    return PRT_RETURN_FALSE;
                }
                else
                {
                    handler->httpErrorCode = 0; // We let the transaction set the error code
                    if (output_token.length > 0 )
                    {
                        handler->pSendAuthHeader = _BuildAuthResponse(protocol_p, 
                                                                      handler->httpErrorCode, &output_token,
                                                                      &handler->sendAuthHeaderLen);
                        if (handler->pSendAuthHeader == NULL)
                        {
                            trace_HTTP_CannotBuildAuthResponse();
                            handler->httpErrorCode = HTTP_ERROR_CODE_INTERNAL_SERVER_ERROR;
                        }
                        (*_g_gssState.Gss_Release_Buffer)(&min_stat, &output_token);
                    }
                    return PRT_RETURN_TRUE;
                }
            }
        }
        else if (GSS_ERROR(maj_stat))
        {
            trace_HTTP_ClientAuthFailed(_StatusString(maj_stat), _StatusString(min_stat));

            if (GSS_ERROR(maj_stat) == GSS_S_NO_CRED ||
                GSS_ERROR(maj_stat) == GSS_S_FAILURE || GSS_ERROR(maj_stat) == GSS_S_UNAUTHORIZED)
            {

                // Unauthorised

                handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
                auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
                response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

                // Problem : 2do complain
                handler->authFailed = TRUE;

            }
            else
            {
                handler->httpErrorCode = HTTP_ERROR_CODE_BAD_REQUEST;
                auth_response = (char *)RESPONSE_HEADER_BAD_REQUEST;
                response_len  = RESPONSE_HEADER_BAD_REQUEST_LEN;
            }

            _SendAuthResponse(handler, auth_response, response_len);
            (* _g_gssState.Gss_Release_Buffer)(&min_stat, &output_token);
            return PRT_RETURN_FALSE;
        }
        else if (maj_stat & GSS_S_CONTINUE_NEEDED)
        {
            trace_HTTP_AuthContinue();
            handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
            if (output_token.length != 0)
            {
                auth_response = _BuildAuthResponse(protocol_p, handler->httpErrorCode, &output_token, &response_len);
                if (auth_response == NULL)
                {

                    // Problem : 2do complain into trace file
                    handler->httpErrorCode = HTTP_ERROR_CODE_INTERNAL_SERVER_ERROR;
                }
                (*_g_gssState.Gss_Release_Buffer)(&min_stat, &output_token);

                _SendAuthResponse(handler, auth_response, response_len);
                PAL_Free(auth_response);
                return PRT_RETURN_FALSE;
            }
            else
            {

                gss_buffer_t user_name = _getPrincipalName(context_hdl);
#define MAX_HOSTNAME_LEN 256
                static char hostname[MAX_HOSTNAME_LEN] = { 0 };
                char *username = (char *)user_name->value;

                int ret = gethostname(hostname, MAX_HOSTNAME_LEN);
                if (ret == 0)
                {
                    if (Strncasecmp(hostname, (char *)username, strlen(hostname)) == 0)
                    {

                        // If the domain is this machine, we can strip the domain name and do a local lookup

                        char *p = memchr(user_name->value,
                                         '\\',
                                         user_name->length);
                        if (p)
                        {
                            username = ++p;
                        }
                    }
                }

                if (0 != LookupUser(username, &handler->authInfo.uid, &handler->authInfo.gid))
                {

                    // After all that, it would be weird for this to fail, but it is possible
                    // on a misconfigured system. either way, if its not there its not there.

                    trace_GetUserUidGid_Failed(headers->username);

                    handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
                    handler->authFailed = TRUE;
                    auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
                    response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

                    _SendAuthResponse(handler, auth_response, response_len);
                    (*_g_gssState.Gss_Delete_Sec_Context)(&min_stat, &context_hdl, NULL);

                    handler->pAuthContext = NULL;
                    handler->authFailed = TRUE;

                    (* _g_gssState.Gss_Release_Cred)(&min_stat, handler->pVerifierCred);
                    handler->pVerifierCred = NULL;

                    (* _g_gssState.Gss_Release_Buffer)(&min_stat, user_name);
                    goto Done;
                }

                if (1 != IsUserAuthorized(username, handler->authInfo.gid))
                {
                    trace_Authorization_Failed(username);
                    
                    handler->httpErrorCode = HTTP_ERROR_CODE_UNAUTHORIZED;
                    auth_response = (char *)RESPONSE_HEADER_UNAUTH_FMT;
                    response_len  = RESPONSE_HEADER_UNAUTH_FMT_LEN;

                    trace_HTTP_UserAuthFailed("NTLM/Kerberos user authorization failed (2)");
                    handler->authFailed = TRUE;
                    _SendAuthResponse(handler, auth_response, response_len);
                    (*_g_gssState.Gss_Delete_Sec_Context)(&min_stat, &context_hdl, NULL);
                    
                    handler->pAuthContext = NULL;
                    
                    (* _g_gssState.Gss_Release_Cred)(&min_stat, handler->pVerifierCred);
                    handler->pVerifierCred = NULL;

                    (* _g_gssState.Gss_Release_Buffer)(&min_stat, user_name);
                    goto Done;
                }

                (* _g_gssState.Gss_Release_Buffer)(&min_stat, user_name);

                handler->httpErrorCode = 0; // We let the transaction set the error code
                handler->isAuthorised = TRUE;
                return PRT_RETURN_TRUE;
            }
        }
    }
#endif

  Done:
    return authorised;
}