/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2008 Apple Inc. All Rights Reserved. * * This file contains Original Code and/or Modifications of Original Code * as defined in and that are subject to the Apple Public Source License * Version 2.0 (the 'License'). You may not use this file except in * compliance with the License. Please obtain a copy of the License at * http://www.opensource.apple.com/apsl/ and read it before using this * file. * * The Original Code and all software distributed under the License are * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. * Please see the License for the specific language governing rights and * limitations under the License. * * @APPLE_LICENSE_HEADER_END@ * */ /* File: QTSSAdminModule.cpp Contains: Implements Admin module */ #ifndef __Win32__ #include /* for getopt() et al */ #endif #include #include /* for qtss_printf */ #include /* for getloadavg & other useful stuff */ #include "QTSSAdminModule.h" #include "OSArrayObjectDeleter.h" #include "StringParser.h" #include "StrPtrLen.h" #include "QTSSModuleUtils.h" #include "OSHashTable.h" #include "OSMutex.h" #include "StrPtrLen.h" #include "OSRef.h" #include "AdminElementNode.h" #include "base64.h" #include "OSMemory.h" #include "md5.h" #include "md5digest.h" #if __MacOSX__ #include #include #endif #if __solaris__ || __linux__ || __sgi__ || __hpux__ #include #endif #define DEBUG_ADMIN_MODULE 0 //************************************************** #define kAuthNameAndPasswordBuffSize 512 #define kPasswordBuffSize kAuthNameAndPasswordBuffSize/2 // STATIC DATA //************************************************** #if DEBUG_ADMIN_MODULE static UInt32 sRequestCount= 0; #endif static QTSS_Initialize_Params sQTSSparams; //static char* sResponseHeader = "HTTP/1.0 200 OK\r\nServer: QTSS\r\nConnection: Close\r\nContent-Type: text/plain\r\n\r\n"; static char* sResponseHeader = "HTTP/1.0 200 OK"; static char* sUnauthorizedResponseHeader = "HTTP/1.1 401 Unauthorized\r\nWWW-Authenticate: Basic realm=\"QTSS/modules/admin\"\r\nServer: QTSS\r\nConnection: Close\r\nContent-Type: text/plain\r\n\r\n"; static char* sPermissionDeniedHeader = "HTTP/1.1 403 Forbidden\r\nConnection: Close\r\nContent-Type: text/html\r\n\r\n"; static char* sHTMLBody = "\n

Your request was denied by the server.

\n\r\n\r\n"; static char* sVersionHeader = NULL; static char* sConnectionHeader = "Connection: Close"; static char* kDefaultHeader = "Server: QTSS"; static char* sContentType = "Content-Type: text/plain"; static char* sEOL = "\r\n"; static char* sEOM = "\r\n\r\n"; static char* sAuthRealm = "QTSS/modules/admin"; static char* sAuthResourceLocalPath = "/modules/admin/"; static QTSS_ServerObject sServer = NULL; static QTSS_ModuleObject sModule = NULL; static QTSS_ModulePrefsObject sModulePrefs = NULL; static QTSS_PrefsObject sServerPrefs = NULL; static AdminClass *sAdminPtr = NULL; static QueryURI *sQueryPtr = NULL; static OSMutex* sAdminMutex = NULL;//admin module isn't reentrant static UInt32 sVersion=20030306; static char *sDesc="Implements HTTP based Admin Protocol for accessing server attributes"; static char decodedLine[kAuthNameAndPasswordBuffSize] = { 0 }; static char codedLine[kAuthNameAndPasswordBuffSize] = { 0 }; static QTSS_TimeVal sLastRequestTime = 0; static UInt32 sSessID = 0; static StrPtrLen sAuthRef("AuthRef"); #if __MacOSX__ static char* sSecurityServerAuthKey = "com.apple.server.admin.streaming"; static AuthorizationItem sRight = { sSecurityServerAuthKey, 0, NULL, 0 }; static AuthorizationRights sRightSet = { 1, &sRight }; #endif // ATTRIBUTES //************************************************** enum { kMaxRequestTimeIntervalMilli = 1000, kDefaultRequestTimeIntervalMilli = 50 }; static UInt32 sDefaultRequestTimeIntervalMilli = kDefaultRequestTimeIntervalMilli; static UInt32 sRequestTimeIntervalMilli = kDefaultRequestTimeIntervalMilli; static Bool16 sAuthenticationEnabled = true; static Bool16 sDefaultAuthenticationEnabled = true; static Bool16 sLocalLoopBackOnlyEnabled = true; static Bool16 sDefaultLocalLoopBackOnlyEnabled = true; static Bool16 sEnableRemoteAdmin = true; static Bool16 sDefaultEnableRemoteAdmin = true; static QTSS_AttributeID sIPAccessListID = qtssIllegalAttrID; static char* sIPAccessList = NULL; static char* sLocalLoopBackAddress = "127.0.0.*"; static char* sAdministratorGroup = NULL; static char* sDefaultAdministratorGroup = "admin"; static Bool16 sFlushing = false; static QTSS_AttributeID sFlushingID = qtssIllegalAttrID; static char* sFlushingName = "QTSSAdminModuleFlushingState"; static UInt32 sFlushingLen = sizeof(sFlushing); static QTSS_AttributeID sAuthenticatedID = qtssIllegalAttrID; static char* sAuthenticatedName = "QTSSAdminModuleAuthenticatedState"; //************************************************** static QTSS_Error QTSSAdminModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams); static QTSS_Error Register(QTSS_Register_Params* inParams); static QTSS_Error Initialize(QTSS_Initialize_Params* inParams); static QTSS_Error FilterRequest(QTSS_Filter_Params* inParams); static QTSS_Error RereadPrefs(); static QTSS_Error AuthorizeAdminRequest(QTSS_RTSPRequestObject request); static Bool16 AcceptSession(QTSS_RTSPSessionObject inRTSPSession); #if !DEBUG_ADMIN_MODULE #define APITests_DEBUG() #define ShowQuery_DEBUG() #else void ShowQuery_DEBUG() { qtss_printf("======REQUEST #%"_U32BITARG_"======\n",++sRequestCount); StrPtrLen* aStr; aStr = sQueryPtr->GetURL(); qtss_printf("URL="); PRINT_STR(aStr); aStr = sQueryPtr->GetQuery(); qtss_printf("Query="); PRINT_STR(aStr); aStr = sQueryPtr->GetParameters(); qtss_printf("Parameters="); PRINT_STR(aStr); aStr = sQueryPtr->GetCommand(); qtss_printf("Command="); PRINT_STR(aStr); qtss_printf("CommandID=%"_S32BITARG_" \n",sQueryPtr->GetCommandID()); aStr = sQueryPtr->GetValue(); qtss_printf("Value="); PRINT_STR(aStr); aStr = sQueryPtr->GetType(); qtss_printf("Type="); PRINT_STR(aStr); aStr = sQueryPtr->GetAccess(); qtss_printf("Access="); PRINT_STR(aStr); } void APITests_DEBUG() { if (0) { qtss_printf("QTSSAdminModule start tests \n"); if (0) { qtss_printf("admin called locked \n"); const int ksleeptime = 15; qtss_printf("sleeping for %d seconds \n",ksleeptime); sleep(ksleeptime); qtss_printf("done sleeping \n"); qtss_printf("QTSS_GlobalUnLock \n"); (void) QTSS_GlobalUnLock(); qtss_printf("again sleeping for %d seconds \n",ksleeptime); sleep(ksleeptime); } if (0) { qtss_printf(" GET VALUE PTR TEST \n"); QTSS_Object *sessionsPtr = NULL; UInt32 paramLen = sizeof(sessionsPtr); UInt32 numValues = 0; QTSS_Error err = 0; err = QTSS_GetNumValues (sServer, qtssSvrClientSessions, &numValues); err = QTSS_GetValuePtr(sServer, qtssSvrClientSessions, 0, (void**)&sessionsPtr, ¶mLen); qtss_printf("Admin Module Num Sessions = %"_U32BITARG_" sessions[0] = %"_S32BITARG_" err = %"_S32BITARG_" paramLen =%"_U32BITARG_"\n", numValues, (SInt32) *sessionsPtr,err,paramLen); UInt32 numAttr = 0; if (sessionsPtr) { err = QTSS_GetNumAttributes (*sessionsPtr, &numAttr); qtss_printf("Admin Module Num attributes = %"_U32BITARG_" sessions[0] = %"_S32BITARG_" err = %"_S32BITARG_"\n", numAttr, (SInt32) *sessionsPtr,err); QTSS_Object theAttributeInfo; char nameBuff[128]; UInt32 len = 127; for (UInt32 i = 0; i < numAttr; i++) { err = QTSS_GetAttrInfoByIndex(*sessionsPtr, i, &theAttributeInfo); nameBuff[0] = 0;len = 127; err = QTSS_GetValue (theAttributeInfo, qtssAttrName,0, nameBuff,&len); nameBuff[len] = 0; qtss_printf("found %s \n",nameBuff); } } } if (0) { qtss_printf(" GET VALUE TEST \n"); QTSS_Object sessions = NULL; UInt32 paramLen = sizeof(sessions); UInt32 numValues = 0; QTSS_Error err = 0; err = QTSS_GetNumValues (sServer, qtssSvrClientSessions, &numValues); err = QTSS_GetValue(sServer, qtssSvrClientSessions, 0, (void*)&sessions, ¶mLen); qtss_printf("Admin Module Num Sessions = %"_U32BITARG_" sessions[0] = %"_S32BITARG_" err = %"_S32BITARG_" paramLen = %"_U32BITARG_"\n", numValues, (SInt32) sessions,err, paramLen); if (sessions) { UInt32 numAttr = 0; err = QTSS_GetNumAttributes (sessions, &numAttr); qtss_printf("Admin Module Num attributes = %"_U32BITARG_" sessions[0] = %"_S32BITARG_" err = %"_S32BITARG_"\n", numAttr,(SInt32) sessions,err); QTSS_Object theAttributeInfo; char nameBuff[128]; UInt32 len = 127; for (UInt32 i = 0; i < numAttr; i++) { err = QTSS_GetAttrInfoByIndex(sessions, i, &theAttributeInfo); nameBuff[0] = 0;len = 127; err = QTSS_GetValue (theAttributeInfo, qtssAttrName,0, nameBuff,&len); nameBuff[len] = 0; qtss_printf("found %s \n",nameBuff); } } } if (0) { qtss_printf("----------------- Start test ----------------- \n"); qtss_printf(" GET indexed pref TEST \n"); QTSS_Error err = 0; UInt32 numAttr = 1; err = QTSS_GetNumAttributes (sModulePrefs, &numAttr); qtss_printf("Admin Module Num preference attributes = %"_U32BITARG_" err = %"_S32BITARG_"\n", numAttr, err); QTSS_Object theAttributeInfo; char valueBuff[512]; char nameBuff[128]; QTSS_AttributeID theID; UInt32 len = 127; UInt32 i = 0; qtss_printf("first pass over preferences\n"); for ( i = 0; i < numAttr; i++) { err = QTSS_GetAttrInfoByIndex(sModulePrefs, i, &theAttributeInfo); nameBuff[0] = 0;len = 127; err = QTSS_GetValue (theAttributeInfo, qtssAttrName,0, nameBuff,&len); nameBuff[len]=0; theID = qtssIllegalAttrID; len = sizeof(theID); err = QTSS_GetValue (theAttributeInfo, qtssAttrID,0, &theID,&len); qtss_printf("found preference=%s \n",nameBuff); } valueBuff[0] = 0;len = 512; err = QTSS_GetValue (sModulePrefs, theID,0, valueBuff,&len);valueBuff[len] = 0; qtss_printf("Admin Module QTSS_GetValue name = %s id = %"_S32BITARG_" value=%s err = %"_S32BITARG_"\n", nameBuff,theID, valueBuff, err); err = QTSS_SetValue (sModulePrefs,theID,0, valueBuff,len); qtss_printf("Admin Module QTSS_SetValue name = %s id = %"_S32BITARG_" value=%s err = %"_S32BITARG_"\n", nameBuff,theID, valueBuff, err); { QTSS_ServiceID id; (void) QTSS_IDForService(QTSS_REREAD_PREFS_SERVICE, &id); (void) QTSS_DoService(id, NULL); } valueBuff[0] = 0;len = 512; err = QTSS_GetValue (sModulePrefs, theID,0, valueBuff,&len);valueBuff[len] = 0; qtss_printf("Admin Module QTSS_GetValue name = %s id = %"_S32BITARG_" value=%s err = %"_S32BITARG_"\n", nameBuff,theID, valueBuff, err); err = QTSS_SetValue (sModulePrefs,theID,0, valueBuff,len); qtss_printf("Admin Module QTSS_SetValue name = %s id = %"_S32BITARG_" value=%s err = %"_S32BITARG_"\n", nameBuff,theID, valueBuff, err); qtss_printf("second pass over preferences\n"); for ( i = 0; i < numAttr; i++) { err = QTSS_GetAttrInfoByIndex(sModulePrefs, i, &theAttributeInfo); nameBuff[0] = 0;len = 127; err = QTSS_GetValue (theAttributeInfo, qtssAttrName,0, nameBuff,&len); nameBuff[len]=0; theID = qtssIllegalAttrID; len = sizeof(theID); err = QTSS_GetValue (theAttributeInfo, qtssAttrID,0, &theID,&len); qtss_printf("found preference=%s \n",nameBuff); } qtss_printf("----------------- Done test ----------------- \n"); } } } #endif inline void KeepSession(QTSS_RTSPRequestObject theRequest,Bool16 keep) { (void)QTSS_SetValue(theRequest, qtssRTSPReqRespKeepAlive, 0, &keep, sizeof(keep)); } // FUNCTION IMPLEMENTATIONS QTSS_Error QTSSAdminModule_Main(void* inPrivateArgs) { return _stublibrary_main(inPrivateArgs, QTSSAdminModuleDispatch); } QTSS_Error QTSSAdminModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams) { switch (inRole) { case QTSS_Register_Role: return Register(&inParams->regParams); case QTSS_Initialize_Role: return Initialize(&inParams->initParams); case QTSS_RTSPFilter_Role: { if (!sEnableRemoteAdmin) break; return FilterRequest(&inParams->rtspFilterParams); } case QTSS_RTSPAuthorize_Role: return AuthorizeAdminRequest(inParams->rtspRequestParams.inRTSPRequest); case QTSS_RereadPrefs_Role: return RereadPrefs(); } return QTSS_NoErr; } QTSS_Error Register(QTSS_Register_Params* inParams) { // Do role & attribute setup (void)QTSS_AddRole(QTSS_Initialize_Role); (void)QTSS_AddRole(QTSS_RTSPFilter_Role); (void)QTSS_AddRole(QTSS_RereadPrefs_Role); (void)QTSS_AddRole(QTSS_RTSPAuthorize_Role); (void)QTSS_AddStaticAttribute(qtssRTSPRequestObjectType, sFlushingName, NULL, qtssAttrDataTypeBool16); (void)QTSS_IDForAttr(qtssRTSPRequestObjectType, sFlushingName, &sFlushingID); (void)QTSS_AddStaticAttribute(qtssRTSPRequestObjectType, sAuthenticatedName, NULL, qtssAttrDataTypeBool16); (void)QTSS_IDForAttr(qtssRTSPRequestObjectType, sAuthenticatedName, &sAuthenticatedID); // Tell the server our name! static char* sModuleName = "QTSSAdminModule"; ::strcpy(inParams->outModuleName, sModuleName); return QTSS_NoErr; } QTSS_Error RereadPrefs() { delete [] sVersionHeader; sVersionHeader = QTSSModuleUtils::GetStringAttribute(sServer, "qtssSvrRTSPServerHeader", kDefaultHeader); delete [] sIPAccessList; sIPAccessList = QTSSModuleUtils::GetStringAttribute(sModulePrefs, "IPAccessList", sLocalLoopBackAddress); sIPAccessListID = QTSSModuleUtils::GetAttrID(sModulePrefs, "IPAccessList"); QTSSModuleUtils::GetAttribute(sModulePrefs, "Authenticate", qtssAttrDataTypeBool16, &sAuthenticationEnabled, &sDefaultAuthenticationEnabled, sizeof(sAuthenticationEnabled)); QTSSModuleUtils::GetAttribute(sModulePrefs, "LocalAccessOnly", qtssAttrDataTypeBool16, &sLocalLoopBackOnlyEnabled, &sDefaultLocalLoopBackOnlyEnabled, sizeof(sLocalLoopBackOnlyEnabled)); QTSSModuleUtils::GetAttribute(sModulePrefs, "RequestTimeIntervalMilli", qtssAttrDataTypeUInt32, &sRequestTimeIntervalMilli, &sDefaultRequestTimeIntervalMilli, sizeof(sRequestTimeIntervalMilli)); QTSSModuleUtils::GetAttribute(sModulePrefs, "enable_remote_admin", qtssAttrDataTypeBool16, &sEnableRemoteAdmin, &sDefaultEnableRemoteAdmin, sizeof(sDefaultEnableRemoteAdmin)); delete [] sAdministratorGroup; sAdministratorGroup = QTSSModuleUtils::GetStringAttribute(sModulePrefs, "AdministratorGroup", sDefaultAdministratorGroup); if (sRequestTimeIntervalMilli > kMaxRequestTimeIntervalMilli) { sRequestTimeIntervalMilli = kMaxRequestTimeIntervalMilli; } (void)QTSS_SetValue(sModule, qtssModDesc, 0, sDesc, strlen(sDesc)+1); (void)QTSS_SetValue(sModule, qtssModVersion, 0, &sVersion, sizeof(sVersion)); return QTSS_NoErr; } QTSS_Error Initialize(QTSS_Initialize_Params* inParams) { sAdminMutex = NEW OSMutex(); ElementNode_InitPtrArray(); // Setup module utils QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream); sQTSSparams = *inParams; sServer = inParams->inServer; sModule = inParams->inModule; sModulePrefs = QTSSModuleUtils::GetModulePrefsObject(sModule); sServerPrefs = inParams->inPrefs; RereadPrefs(); return QTSS_NoErr; } void ReportErr(QTSS_Filter_Params* inParams, UInt32 err) { StrPtrLen* urlPtr = sQueryPtr->GetURL(); StrPtrLen* evalMessagePtr = sQueryPtr->GetEvalMsg(); char temp[32]; if (urlPtr && evalMessagePtr) { qtss_sprintf(temp,"(%"_U32BITARG_")",err); (void)QTSS_Write(inParams->inRTSPRequest, "error:", strlen("error:"), NULL, 0); (void)QTSS_Write(inParams->inRTSPRequest, temp, strlen(temp), NULL, 0); if (sQueryPtr->VerboseParam()) { (void)QTSS_Write(inParams->inRTSPRequest, ";URL=", strlen(";URL="), NULL, 0); if (urlPtr) (void)QTSS_Write(inParams->inRTSPRequest, urlPtr->Ptr, urlPtr->Len, NULL, 0); } if (sQueryPtr->DebugParam()) { (void)QTSS_Write(inParams->inRTSPRequest, ";", strlen(";"), NULL, 0); (void)QTSS_Write(inParams->inRTSPRequest, evalMessagePtr->Ptr, evalMessagePtr->Len, NULL, 0); } (void)QTSS_Write(inParams->inRTSPRequest, "\r\n\r\n", 4, NULL, 0); } } inline Bool16 AcceptAddress(StrPtrLen *theAddressPtr) { IPComponentStr ipComponentStr(theAddressPtr); Bool16 isLocalRequest = ipComponentStr.IsLocal(); if (sLocalLoopBackOnlyEnabled && isLocalRequest) return true; if (sLocalLoopBackOnlyEnabled && !isLocalRequest) return false; if (QTSSModuleUtils::AddressInList(sModulePrefs, sIPAccessListID, theAddressPtr)) return true; return false; } inline Bool16 IsAdminRequest(StringParser *theFullRequestPtr) { Bool16 handleRequest = false; if (theFullRequestPtr != NULL) do { StrPtrLen strPtr; theFullRequestPtr->ConsumeWord(&strPtr); if ( !strPtr.Equal(StrPtrLen("GET")) ) break; //it's a "Get" request theFullRequestPtr->ConsumeWhitespace(); if ( !theFullRequestPtr->Expect('/') ) break; theFullRequestPtr->ConsumeWord(&strPtr); if ( strPtr.Len == 0 || !strPtr.Equal(StrPtrLen("modules") ) ) break; if (!theFullRequestPtr->Expect('/') ) break; theFullRequestPtr->ConsumeWord(&strPtr); if ( strPtr.Len == 0 || !strPtr.Equal(StrPtrLen("admin") ) ) break; handleRequest = true; } while (false); return handleRequest; } inline void ParseAuthNameAndPassword(StrPtrLen *codedStrPtr, StrPtrLen* namePtr, StrPtrLen* passwordPtr) { if (!codedStrPtr || (codedStrPtr->Len >= kAuthNameAndPasswordBuffSize) ) { return; } StrPtrLen codedLineStr; StrPtrLen nameAndPassword; memset(decodedLine,0,kAuthNameAndPasswordBuffSize); memset(codedLine,0,kAuthNameAndPasswordBuffSize); memcpy (codedLine,codedStrPtr->Ptr,codedStrPtr->Len); codedLineStr.Set((char*) codedLine, codedStrPtr->Len); (void) Base64decode(decodedLine, codedLineStr.Ptr); nameAndPassword.Set((char*) decodedLine, strlen(decodedLine)); StringParser parsedNameAndPassword(&nameAndPassword); parsedNameAndPassword.ConsumeUntil(namePtr,':'); parsedNameAndPassword.ConsumeLength(NULL, 1); // password can have whitespace, so read until the end of the line, not just until whitespace parsedNameAndPassword.ConsumeUntil(passwordPtr, StringParser::sEOLMask); namePtr->Ptr[namePtr->Len]= 0; passwordPtr->Ptr[passwordPtr->Len]= 0; //qtss_printf("decoded nameAndPassword="); PRINT_STR(&nameAndPassword); //qtss_printf("decoded name="); PRINT_STR(namePtr); //qtss_printf("decoded password="); PRINT_STR(passwordPtr); return; }; inline Bool16 OSXAuthenticate(StrPtrLen *keyStrPtr) { #if __MacOSX__ // Authorization: AuthRef QWxhZGRpbjpvcGVuIHNlc2FtZQ== Bool16 result = false; if (keyStrPtr == NULL || keyStrPtr->Len == 0) return result; char *encodedKey = keyStrPtr->GetAsCString(); OSCharArrayDeleter encodedKeyDeleter(encodedKey); char *decodedKey = NEW char[Base64decode_len(encodedKey) + 1]; OSCharArrayDeleter decodedKeyDeleter(decodedKey); (void) Base64decode(decodedKey, encodedKey); AuthorizationExternalForm *receivedExtFormPtr = (AuthorizationExternalForm *) decodedKey; AuthorizationRef receivedAuthorization; OSStatus status = AuthorizationCreateFromExternalForm(receivedExtFormPtr, &receivedAuthorization); if (status != errAuthorizationSuccess) return result; status = AuthorizationCopyRights(receivedAuthorization, &sRightSet, kAuthorizationEmptyEnvironment, kAuthorizationFlagExtendRights , NULL); if (status == errAuthorizationSuccess) { result = true; } AuthorizationFree(receivedAuthorization, kAuthorizationFlagDestroyRights); return result; #else return false; #endif } inline Bool16 HasAuthentication(StringParser *theFullRequestPtr, StrPtrLen* namePtr, StrPtrLen* passwordPtr, StrPtrLen* outAuthTypePtr) { // Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== Bool16 hasAuthentication = false; StrPtrLen strPtr; StrPtrLen authType; StrPtrLen authString; while (theFullRequestPtr->GetDataRemaining() > 0) { theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntilWhitespace(&strPtr); if ( strPtr.Len == 0 || !strPtr.Equal(StrPtrLen("Authorization:")) ) continue; theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntilWhitespace(&authType); if ( authType.Len == 0 ) continue; theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntil(&authString, StringParser::sEOLMask); if ( authString.Len == 0 ) continue; if (outAuthTypePtr != NULL) outAuthTypePtr->Set(authType.Ptr, authType.Len); if (authType.Equal(StrPtrLen("Basic") ) ) { (void) ParseAuthNameAndPassword(&authString,namePtr, passwordPtr); if (namePtr->Len == 0) continue; hasAuthentication = true; break; } else if (authType.Equal(sAuthRef) ) { namePtr->Set(NULL,0); passwordPtr->Set(authString.Ptr, authString.Len); hasAuthentication = true; break; } }; return hasAuthentication; } Bool16 Authenticate(QTSS_RTSPRequestObject request, StrPtrLen* namePtr, StrPtrLen* passwordPtr) { Bool16 authenticated = true; char* authName = namePtr->GetAsCString(); OSCharArrayDeleter authNameDeleter(authName); QTSS_ActionFlags authAction = qtssActionFlagsAdmin; // authenticate callback to retrieve the password QTSS_Error err = QTSS_Authenticate(authName, sAuthResourceLocalPath, sAuthResourceLocalPath, authAction, qtssAuthBasic, request); if (err != QTSS_NoErr) { return false; // Couldn't even call QTSS_Authenticate...abandon! } // Get the user profile object from the request object that was created in the authenticate callback QTSS_UserProfileObject theUserProfile = NULL; UInt32 len = sizeof(QTSS_UserProfileObject); err = QTSS_GetValue(request, qtssRTSPReqUserProfile, 0, (void*)&theUserProfile, &len); Assert(len == sizeof(QTSS_UserProfileObject)); if (err != QTSS_NoErr) authenticated = false; if(err == QTSS_NoErr) { char* reqPassword = passwordPtr->GetAsCString(); OSCharArrayDeleter reqPasswordDeleter(reqPassword); char* userPassword = NULL; (void) QTSS_GetValueAsString(theUserProfile, qtssUserPassword, 0, &userPassword); OSCharArrayDeleter userPasswordDeleter(userPassword); if(userPassword == NULL) { authenticated = false; } else { #ifdef __Win32__ // The password is md5 encoded for win32 char md5EncodeResult[120]; MD5Encode(reqPassword, userPassword, md5EncodeResult, sizeof(md5EncodeResult)); if(::strcmp(userPassword, md5EncodeResult) != 0) authenticated = false; #else if(::strcmp(userPassword, (char*)crypt(reqPassword, userPassword)) != 0) authenticated = false; #endif } } char* realm = NULL; Bool16 allowed = true; //authorize callback to check authorization // allocates memory for realm err = QTSS_Authorize(request, &realm, &allowed); // QTSS_Authorize allocates memory for the realm string // we don't use the realm returned by the callback, but instead // use our own. // delete the memory allocated for realm because we don't need it! OSCharArrayDeleter realmDeleter(realm); if(err != QTSS_NoErr) { qtss_printf("QTSSAdminModule::Authenticate: QTSS_Authorize failed\n"); return false; // Couldn't even call QTSS_Authorize...abandon! } if(authenticated && allowed) return true; return false; } QTSS_Error AuthorizeAdminRequest(QTSS_RTSPRequestObject request) { Bool16 allowed = false; // get the resource path // if the path does not match the admin path, don't handle the request char* resourcePath = QTSSModuleUtils::GetLocalPath_Copy(request); OSCharArrayDeleter resourcePathDeleter(resourcePath); if(strcmp(sAuthResourceLocalPath, resourcePath) != 0) return QTSS_NoErr; // get the type of request QTSS_ActionFlags action = QTSSModuleUtils::GetRequestActions(request); if(!(action & qtssActionFlagsAdmin)) return QTSS_RequestFailed; QTSS_UserProfileObject theUserProfile = QTSSModuleUtils::GetUserProfileObject(request); if (NULL == theUserProfile) return QTSS_RequestFailed; (void) QTSS_SetValue(request,qtssRTSPReqURLRealm, 0, sAuthRealm, ::strlen(sAuthRealm)); // Authorize the user if the user belongs to the AdministratorGroup (this is an admin module pref) UInt32 numGroups = 0; char** groupsArray = QTSSModuleUtils::GetGroupsArray_Copy(theUserProfile, &numGroups); if ((groupsArray != NULL) && (numGroups != 0)) { UInt32 index = 0; for (index = 0; index < numGroups; index++) { if(strcmp(sAdministratorGroup, groupsArray[index]) == 0) { allowed = true; break; } } // delete the memory allocated in QTSSModuleUtils::GetGroupsArray_Copy call delete [] groupsArray; } if(!allowed) { if (QTSS_NoErr != QTSS_SetValue(request,qtssRTSPReqUserAllowed, 0, &allowed, sizeof(allowed))) return QTSS_RequestFailed; // Bail on the request. The Server will handle the error } return QTSS_NoErr; } Bool16 AcceptSession(QTSS_RTSPSessionObject inRTSPSession) { char remoteAddress[20] = {0}; StrPtrLen theClientIPAddressStr(remoteAddress,sizeof(remoteAddress)); QTSS_Error err = QTSS_GetValue(inRTSPSession, qtssRTSPSesRemoteAddrStr, 0, (void*)theClientIPAddressStr.Ptr, &theClientIPAddressStr.Len); if (err != QTSS_NoErr) return false; return AcceptAddress(&theClientIPAddressStr); } Bool16 StillFlushing(QTSS_Filter_Params* inParams,Bool16 flushing) { QTSS_Error err = QTSS_NoErr; if (flushing) { err = QTSS_Flush(inParams->inRTSPRequest); //qtss_printf("Flushing session=%"_U32BITARG_" QTSS_Flush err =%"_S32BITARG_"\n",sSessID,err); } if (err == QTSS_WouldBlock) // more to flush later { sFlushing = true; (void) QTSS_SetValue(inParams->inRTSPRequest, sFlushingID, 0, (void*)&sFlushing, sFlushingLen); err = QTSS_RequestEvent(inParams->inRTSPRequest, QTSS_WriteableEvent); KeepSession(inParams->inRTSPRequest,true); //qtss_printf("Flushing session=%"_U32BITARG_" QTSS_RequestEvent err =%"_S32BITARG_"\n",sSessID,err); } else { sFlushing = false; (void) QTSS_SetValue(inParams->inRTSPRequest, sFlushingID, 0, (void*)&sFlushing, sFlushingLen); KeepSession(inParams->inRTSPRequest,false); if (flushing) // we were flushing so reset the LastRequestTime { sLastRequestTime = QTSS_Milliseconds(); //qtss_printf("Done Flushing session=%"_U32BITARG_"\n",sSessID); return true; } } return sFlushing; } Bool16 IsAuthentic(QTSS_Filter_Params* inParams,StringParser *fullRequestPtr) { Bool16 isAuthentic = false; if (!sAuthenticationEnabled) // no authentication { isAuthentic = true; } else // must authenticate { StrPtrLen theClientIPAddressStr; (void) QTSS_GetValuePtr(inParams->inRTSPSession, qtssRTSPSesRemoteAddrStr, 0, (void**)&theClientIPAddressStr.Ptr, &theClientIPAddressStr.Len); Bool16 isLocal = IPComponentStr(&theClientIPAddressStr).IsLocal(); StrPtrLen authenticateName; StrPtrLen authenticatePassword; StrPtrLen authType; Bool16 hasAuthentication = HasAuthentication(fullRequestPtr,&authenticateName,&authenticatePassword, &authType); if (hasAuthentication) { if (authType.Equal(sAuthRef)) { if (isLocal) isAuthentic = OSXAuthenticate(&authenticatePassword); } else isAuthentic = Authenticate(inParams->inRTSPRequest, &authenticateName,&authenticatePassword); } } // if (isAuthentic) // isAuthentic = AuthorizeAdminRequest(inParams->inRTSPRequest); (void) QTSS_SetValue(inParams->inRTSPRequest, sAuthenticatedID, 0, (void*)&isAuthentic, sizeof(isAuthentic)); return isAuthentic; } inline Bool16 InWaitInterval(QTSS_Filter_Params* inParams) { QTSS_TimeVal nextExecuteTime = sLastRequestTime + sRequestTimeIntervalMilli; QTSS_TimeVal currentTime = QTSS_Milliseconds(); SInt32 waitTime = 0; if (currentTime < nextExecuteTime) { waitTime = (SInt32) (nextExecuteTime - currentTime) + 1; //qtss_printf("(currentTime < nextExecuteTime) sSessID = %"_U32BITARG_" waitTime =%"_S32BITARG_" currentTime = %qd nextExecute = %qd interval=%"_U32BITARG_"\n",sSessID, waitTime, currentTime, nextExecuteTime,sRequestTimeIntervalMilli); (void)QTSS_SetIdleTimer(waitTime); KeepSession(inParams->inRTSPRequest,true); //qtss_printf("-- call me again after %"_S32BITARG_" millisecs session=%"_U32BITARG_" \n",waitTime,sSessID); return true; } sLastRequestTime = QTSS_Milliseconds(); //qtss_printf("handle sessID=%"_U32BITARG_" time=%qd \n",sSessID,currentTime); return false; } inline void GetQueryData(QTSS_RTSPRequestObject theRequest) { sAdminPtr = NEW AdminClass(); Assert(sAdminPtr != NULL); if (sAdminPtr == NULL) { //qtss_printf ("NEW AdminClass() failed!! \n"); return; } if (sAdminPtr != NULL) { sAdminPtr->Initialize(&sQTSSparams,sQueryPtr); // Get theData } } inline void SendHeader(QTSS_StreamRef inStream) { (void)QTSS_Write(inStream, sResponseHeader, ::strlen(sResponseHeader), NULL, 0); (void)QTSS_Write(inStream, sEOL, ::strlen(sEOL), NULL, 0); (void)QTSS_Write(inStream, sVersionHeader, ::strlen(sVersionHeader), NULL, 0); (void)QTSS_Write(inStream, sEOL, ::strlen(sEOL), NULL, 0); (void)QTSS_Write(inStream, sConnectionHeader, ::strlen(sConnectionHeader), NULL, 0); (void)QTSS_Write(inStream, sEOL, ::strlen(sEOL), NULL, 0); (void)QTSS_Write(inStream, sContentType, ::strlen(sContentType), NULL, 0); (void)QTSS_Write(inStream, sEOM, ::strlen(sEOM), NULL, 0); } inline void SendResult(QTSS_StreamRef inStream) { SendHeader(inStream); if (sAdminPtr != NULL) sAdminPtr->RespondToQuery(inStream,sQueryPtr,sQueryPtr->GetRootID()); } inline Bool16 GetRequestAuthenticatedState(QTSS_Filter_Params* inParams) { Bool16 result = false; UInt32 paramLen = sizeof(result); QTSS_Error err = QTSS_GetValue(inParams->inRTSPRequest, sAuthenticatedID, 0, (void*)&result, ¶mLen); if(err != QTSS_NoErr) { paramLen = sizeof(result); result = false; err =QTSS_SetValue(inParams->inRTSPRequest, sAuthenticatedID, 0, (void*)&result, paramLen); } return result; } inline Bool16 GetRequestFlushState(QTSS_Filter_Params* inParams) { Bool16 result = false; UInt32 paramLen = sizeof(result); QTSS_Error err = QTSS_GetValue(inParams->inRTSPRequest, sFlushingID, 0, (void*)&result, ¶mLen); if (err != QTSS_NoErr) { paramLen = sizeof(result); result = false; //qtss_printf("no flush val so set to false session=%"_U32BITARG_" err =%"_S32BITARG_"\n",sSessID, err); err =QTSS_SetValue(inParams->inRTSPRequest, sFlushingID, 0, (void*)&result, paramLen); //qtss_printf("QTSS_SetValue flush session=%"_U32BITARG_" err =%"_S32BITARG_"\n",sSessID, err); } return result; } QTSS_Error FilterRequest(QTSS_Filter_Params* inParams) { if (NULL == inParams || NULL == inParams->inRTSPSession || NULL == inParams->inRTSPRequest) { Assert(0); return QTSS_NoErr; } OSMutexLocker locker(sAdminMutex); //check to see if we should handle this request. Invokation is triggered //by a "GET /" request QTSS_Error err = QTSS_NoErr; QTSS_RTSPRequestObject theRequest = inParams->inRTSPRequest; UInt32 paramLen = sizeof(sSessID); err = QTSS_GetValue(inParams->inRTSPSession, qtssRTSPSesID, 0, (void*)&sSessID, ¶mLen); if (err != QTSS_NoErr) return QTSS_NoErr; StrPtrLen theFullRequest; err = QTSS_GetValuePtr(theRequest, qtssRTSPReqFullRequest, 0, (void**)&theFullRequest.Ptr, &theFullRequest.Len); if (err != QTSS_NoErr) return QTSS_NoErr; StringParser fullRequest(&theFullRequest); if ( !IsAdminRequest(&fullRequest) ) return QTSS_NoErr; if ( !AcceptSession(inParams->inRTSPSession) ) { (void)QTSS_Write(inParams->inRTSPRequest, sPermissionDeniedHeader, ::strlen(sPermissionDeniedHeader), NULL, 0); (void)QTSS_Write(inParams->inRTSPRequest, sHTMLBody, ::strlen(sHTMLBody), NULL, 0); KeepSession(theRequest,false); return QTSS_NoErr; } if(!GetRequestAuthenticatedState(inParams)) // must authenticate before handling { if(QTSS_IsGlobalLocked()) // must NOT be global locked return QTSS_RequestFailed; if (!IsAuthentic(inParams,&fullRequest)) { (void)QTSS_Write(inParams->inRTSPRequest, sUnauthorizedResponseHeader, ::strlen(sUnauthorizedResponseHeader), NULL, 0); (void)QTSS_Write(inParams->inRTSPRequest, sHTMLBody, ::strlen(sHTMLBody), NULL, 0); KeepSession(theRequest,false); return QTSS_NoErr; } } if (GetRequestFlushState(inParams)) { StillFlushing(inParams,true); return QTSS_NoErr; } if (!QTSS_IsGlobalLocked()) { if (InWaitInterval(inParams)) return QTSS_NoErr; //qtss_printf("New Request Wait for GlobalLock session=%"_U32BITARG_"\n",sSessID); (void)QTSS_RequestGlobalLock(); KeepSession(theRequest,true); return QTSS_NoErr; } //qtss_printf("Handle request session=%"_U32BITARG_"\n",sSessID); APITests_DEBUG(); if (sQueryPtr != NULL) { delete sQueryPtr; sQueryPtr = NULL; } sQueryPtr = NEW QueryURI(&theFullRequest); if (sQueryPtr == NULL) return QTSS_NoErr; ShowQuery_DEBUG(); if (sAdminPtr != NULL) { delete sAdminPtr; sAdminPtr = NULL; } UInt32 result = sQueryPtr->EvalQuery(NULL, NULL); if (result == 0) do { if( ElementNode_CountPtrs() > 0) { ElementNode_ShowPtrs(); Assert(0); } GetQueryData(theRequest); SendResult(theRequest); delete sAdminPtr; sAdminPtr = NULL; if (sQueryPtr && !sQueryPtr->QueryHasReponse()) { UInt32 err = 404; (void) sQueryPtr->EvalQuery(&err,NULL); ReportErr(inParams, err); break; } if (sQueryPtr && sQueryPtr->QueryHasReponse()) { ReportErr(inParams, sQueryPtr->GetEvaluResult()); } if (sQueryPtr->fIsPref && sQueryPtr->GetEvaluResult() == 0) { QTSS_ServiceID id; (void) QTSS_IDForService(QTSS_REREAD_PREFS_SERVICE, &id); (void) QTSS_DoService(id, NULL); } } while(false); else { SendHeader(theRequest); ReportErr(inParams, sQueryPtr->GetEvaluResult()); } if (sQueryPtr != NULL) { delete sQueryPtr; sQueryPtr = NULL; } (void) StillFlushing(inParams,true); return QTSS_NoErr; }