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;
}