/* * * @APPLE_LICENSE_HEADER_START@ * * Copyright (c) 1999-2001 Apple Computer, Inc. All Rights Reserved. The * contents of this file constitute Original Code as defined in and are * subject to the Apple Public Source License Version 1.2 (the 'License'). * You may not use this file except in compliance with the License. Please * obtain a copy of the License at http://www.apple.com/publicsource and * read it before using this file. * * This 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 $Log: QTSSAdminModule.cpp,v $ Revision 1.28.6.1 2002/07/11 22:53:30 mythili leaks bug fixes 2988356 and 2988362 Revision 1.28 2001/10/12 22:13:22 sussery add HTML body to auth failed message. Bug #: Submitted by: Reviewed by: Revision 1.27 2001/10/08 18:05:26 mythili changed QTSSModuleUtils calls from Get***Pref*** to Get***Attribute*** Revision 1.26 2001/08/29 17:35:03 mythili changed admin module to accept any valid user that belongs to the group given by the AdministratorGroup pref Revision 1.25 2001/08/03 07:01:28 murata fix build for RH linux 7.2 Bug #: Submitted by: Reviewed by: Revision 1.24 2001/08/02 19:09:02 murata Fix bad ModuleUtils initialize calls with bad parameter. Bug #: Submitted by: Reviewed by: Revision 1.23 2001/06/07 19:49:14 sussery removed obsolete MacOSXServer code Bug #: Submitted by: Reviewed by: Revision 1.22 2001/03/16 22:22:16 mythili took out AdministratorPassword pref from admin module Revision 1.21 2001/03/13 22:24:03 murata Replace copyright notice for license 1.0 with license 1.2 and update the copyright year. Bug #: Submitted by: Reviewed by: Revision 1.20 2001/03/12 22:20:51 mythili fixed error in parsing authname and password code in the admin module, and setting of passwords with whitespace through the web admin for the administrator password Revision 1.19 2001/02/21 01:14:06 mythili memory leak fix for realm ptr Revision 1.18 2001/02/20 23:13:00 murata Fix authentication enabled pref and cleanup. Bug #: Submitted by: Reviewed by: Revision 1.17 2001/02/20 17:49:13 mythili include crypt.h for solaris Revision 1.16 2001/02/19 23:43:20 murata Fix memleaks Bug #: Submitted by: Reviewed by: Revision 1.15 2001/02/18 23:08:28 mythili modified admin module to use auth callbacks Revision 1.14 2001/02/16 00:55:05 murata Add request denied html as feedback for access disabled. Bug #: Submitted by: Reviewed by: Revision 1.13 2001/01/30 22:21:23 murata Reorganize and clean up test code. Default local loopback to 127.0.0.* instead of 127.*.*.* Bug #: Submitted by: Reviewed by: Revision 1.12 2001/01/28 05:11:38 murata Default to authentication required. Bug #: Submitted by: Reviewed by: Revision 1.11 2001/01/28 04:47:58 murata Remove mem leaks and clean up preference code to use the ModuleUtils routines. Bug #: Submitted by: Reviewed by: Revision 1.10 2001/01/22 22:10:41 murata Fix remote access to admin module. Bug #: Submitted by: Reviewed by: Revision 1.9 2000/12/20 21:29:11 mythili fixed the typo in earlier fix Revision 1.8 2000/12/20 21:11:12 mythili fixed minor but quicklook blck bug Revision 1.7 2000/12/19 00:56:57 murata Add IPAccessList to prefs file if it isn't found. Bug #: Submitted by: Reviewed by: Revision 1.6 2000/12/01 00:49:51 murata update prefs for enabling and disabling features. Bug #: Submitted by: Reviewed by: Revision 1.5 2000/11/16 22:47:11 murata Add module description and up the version. Bug #: Submitted by: Reviewed by: Revision 1.4 2000/10/24 19:59:44 serenyi Updated to reflect latest QTSS API changes Revision 1.3 2000/10/13 08:20:27 serenyi import Revision 1.7 2000/10/05 04:07:25 mythili john added line to close session after sending 401 response Revision 1.6 2000/09/29 06:41:06 serenyi Working on getting Win32 server to compile Revision 1.5 2000/09/29 01:02:22 murata Change password pref name to "AdministratorPassword" Revision 1.4 2000/09/27 21:10:53 lecroy change AdministratorPassword back to Administrator Revision 1.3 2000/09/26 01:44:03 lecroy renamed attribute to Revision 1.2 2000/09/25 22:29:08 serenyi Got rid of permissions argument to QTSS_Add***Attribute Revision 1.1.1.1 2000/08/31 00:30:22 serenyi Mothra Repository Revision 1.18 2000/08/22 13:49:54 murata Get the server's version header. Revision 1.17 2000/08/22 01:14:02 murata No features or bug fixes just some code cleanup. Revision 1.16 2000/08/15 22:49:19 murata Change version #. Revision 1.14 2000/08/15 02:13:28 murata *** empty log message *** Revision 1.13 2000/08/15 01:01:35 murata Change from SInt64 to UInt32 for millisecond time value. Revision 1.12 2000/08/11 19:54:28 murata Change version and turn on local only as default. Optimize query data gathering. Revision 1.11 2000/08/10 01:11:10 murata Latest changes. Revision 1.10 2000/08/05 02:45:01 murata Fix constant and resynch with API changes. Revision 1.9 2000/08/05 01:51:29 murata Change version to today's date. Revision 1.8 2000/08/05 01:46:36 murata Fix some bugs. Revision 1.7 2000/08/03 00:20:27 murata Change version Revision 1.6 2000/08/03 00:18:32 murata Fix bugs. Revision 1.5 2000/07/28 12:35:38 murata Fix to call re-read prefs for adding to array lists. Added local loop back check and IP address list filter. Revision 1.4 2000/07/27 02:39:29 murata up the version number. Revision 1.3 2000/07/27 02:38:57 murata Added feature: HTTP 1.1 Basic authentication Added feature: module preference to enable authentication (off by default for now will change later) Added feature: Administrator password as module preference (NULL by default) Added feature: filter1 through filter10 for get commands (no filter = all) Fixed bug: re-read preferences service is called only on set. Revision 1.2 2000/07/26 01:10:13 murata Fix update of preferences by calling re-read prefs service. Revision 1.1 2000/07/24 23:50:25 murata tuff for Ghidra/Rodan Revision 1.2 2000/06/29 23:54:56 serenyi Fixes for Win32 build Revision 1.1 2000/06/17 04:20:11 murata Add QTSSAdminModule and fix Project Builder,Jam and Posix Make builds. Bug #: Submitted by: Reviewed by: */ #ifndef __Win32__ #include /* for getopt() et al */ #endif #include #include /* for 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 __solaris__ || __linux__ #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=11162000; 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; // 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"; #define kNumComponents 4 static StrPtrLen sLocalLoopComponents[kNumComponents]; #define kRemoteAddressSize 20 //static char* sAdministrator = "streamingadmin"; 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_StandardRTSP_Params* inParams); static Bool16 IsValidAddress(char *theAddressPtr, StrPtrLen *parseResultPtr); #define SetValidAddress(theAddressPtr, parseResultPtr) IsValidAddress(theAddressPtr, parseResultPtr); static Bool16 AddressAllowed(StrPtrLen *testPtr, StrPtrLen *allowedPtr); #if !DEBUG_ADMIN_MODULE #define APITests_DEBUG() #define ShowQuery_DEBUG() #else void ShowQuery_DEBUG() { printf("======REQUEST #%lu======\n",++sRequestCount); StrPtrLen* aStr; aStr = sQueryPtr->GetURL(); printf("URL="); PRINT_STR(aStr); aStr = sQueryPtr->GetQuery(); printf("Query="); PRINT_STR(aStr); aStr = sQueryPtr->GetParameters(); printf("Parameters="); PRINT_STR(aStr); aStr = sQueryPtr->GetCommand(); printf("Command="); PRINT_STR(aStr); printf("CommandID=%ld \n",sQueryPtr->GetCommandID()); aStr = sQueryPtr->GetValue(); printf("Value="); PRINT_STR(aStr); aStr = sQueryPtr->GetType(); printf("Type="); PRINT_STR(aStr); aStr = sQueryPtr->GetAccess(); printf("Access="); PRINT_STR(aStr); } void APITests_DEBUG() { if (0) { printf("QTSSAdminModule start tests \n"); if (0) { printf("admin called locked \n"); const int ksleeptime = 15; printf("sleeping for %d seconds \n",ksleeptime); sleep(ksleeptime); printf("done sleeping \n"); printf("QTSS_GlobalUnLock \n"); (void) QTSS_GlobalUnLock(); printf("again sleeping for %d seconds \n",ksleeptime); sleep(ksleeptime); } if (0) { 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); printf("Admin Module Num Sessions = %lu sessions[0] = %ld err = %ld paramLen =%lu\n", numValues, (SInt32) *sessionsPtr,err,paramLen); UInt32 numAttr = 0; if (sessionsPtr) { err = QTSS_GetNumAttributes (*sessionsPtr, &numAttr); printf("Admin Module Num attributes = %lu sessions[0] = %ld err = %ld\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; printf("found %s \n",nameBuff); } } } if (0) { 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); printf("Admin Module Num Sessions = %lu sessions[0] = %ld err = %ld paramLen = %lu\n", numValues, (SInt32) sessions,err, paramLen); if (sessions) { UInt32 numAttr = 0; err = QTSS_GetNumAttributes (sessions, &numAttr); printf("Admin Module Num attributes = %lu sessions[0] = %ld err = %ld\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; printf("found %s \n",nameBuff); } } } if (0) { printf("----------------- Start test ----------------- \n"); printf(" GET indexed pref TEST \n"); QTSS_Error err = 0; UInt32 numAttr = 1; err = QTSS_GetNumAttributes (sModulePrefs, &numAttr); printf("Admin Module Num preference attributes = %lu err = %ld\n", numAttr, err); QTSS_Object theAttributeInfo; char valueBuff[512]; char nameBuff[128]; QTSS_AttributeID theID; UInt32 len = 127; UInt32 i = 0; 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); printf("found preference=%s \n",nameBuff); } valueBuff[0] = 0;len = 512; err = QTSS_GetValue (sModulePrefs, theID,0, valueBuff,&len);valueBuff[len] = 0; printf("Admin Module QTSS_GetValue name = %s id = %ld value=%s err = %ld\n", nameBuff,theID, valueBuff, err); err = QTSS_SetValue (sModulePrefs,theID,0, valueBuff,len); printf("Admin Module QTSS_SetValue name = %s id = %ld value=%s err = %ld\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; printf("Admin Module QTSS_GetValue name = %s id = %ld value=%s err = %ld\n", nameBuff,theID, valueBuff, err); err = QTSS_SetValue (sModulePrefs,theID,0, valueBuff,len); printf("Admin Module QTSS_SetValue name = %s id = %ld value=%s err = %ld\n", nameBuff,theID, valueBuff, err); 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); printf("found preference=%s \n",nameBuff); } 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); 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); (void) SetValidAddress(sLocalLoopBackAddress, &sLocalLoopComponents[0]); 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) { sprintf(temp,"(%lu)",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); } } Bool16 IsValidAddress(char *theAddressPtr, StrPtrLen *parseResultPtr) { Bool16 valid = false; StrPtrLen sourceStr(theAddressPtr); StringParser IP_Paser(&sourceStr); StrPtrLen *piecePtr = parseResultPtr; while (IP_Paser.GetDataRemaining() > 0) { IP_Paser.ConsumeUntil(piecePtr,'.'); if (piecePtr->Len == 0) break; IP_Paser.ConsumeLength(NULL, 1); if (piecePtr == &parseResultPtr[kNumComponents -1]) { valid = true; break; } piecePtr++; }; return valid; } Bool16 AddressAllowed(StrPtrLen *testPtr, StrPtrLen *allowedPtr) { Bool16 allowed = false; SInt16 component= 0; for (; component < 4 ; component ++) { if (allowedPtr->Ptr[0] != '*' || allowedPtr->Len != 1) if ( !testPtr->Equal(*allowedPtr) ) break; allowedPtr ++; testPtr ++; }; if (component == 4) { allowed = true; } return allowed; } inline Bool16 AcceptAddress(char *theAddressPtr) { StrPtrLen ipComponentTest[kNumComponents]; if (!IsValidAddress(theAddressPtr, &ipComponentTest[0]) ) return false; Bool16 isLocalRequest = AddressAllowed(&ipComponentTest[0], &sLocalLoopComponents[0]); if (isLocalRequest) return true; if (sLocalLoopBackOnlyEnabled && !isLocalRequest) return false; StrPtrLen ipComponentPref[kNumComponents]; UInt32 paramLen; QTSS_Error err; UInt32 numValues = 0; (void) QTSS_GetNumValues (sModulePrefs, sIPAccessListID, &numValues); char acceptAddress[kRemoteAddressSize] = {0}; while (numValues > 0) { numValues --; paramLen = kRemoteAddressSize; err = QTSS_GetValue(sModulePrefs, sIPAccessListID, numValues, (void*)acceptAddress, ¶mLen); if (paramLen == 0 || err != QTSS_NoErr) continue; acceptAddress[paramLen] = 0; if (!IsValidAddress(acceptAddress, &ipComponentPref[0])) continue; if (AddressAllowed(&ipComponentTest[0], &ipComponentPref[0])) 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); //parsedNameAndPassword.ConsumeUntilWhitespace(passwordPtr); // 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; //printf("decoded nameAndPassword="); PRINT_STR(&nameAndPassword); //printf("decoded name="); PRINT_STR(namePtr); //printf("decoded password="); PRINT_STR(passwordPtr); return; }; inline Bool16 HasAuthentication(StringParser *theFullRequestPtr, StrPtrLen* namePtr, StrPtrLen* passwordPtr) { // Authorization: Basic QWxhZGRpbjpvcGVuIHNlc2FtZQ== Bool16 hasAuthentication = false; StrPtrLen strPtr; while (theFullRequestPtr->GetDataRemaining() > 0) { theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntilWhitespace(&strPtr); if ( strPtr.Len == 0 || !strPtr.Equal(StrPtrLen("Authorization:")) ) continue; theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntilWhitespace(&strPtr); if ( strPtr.Len == 0 || !strPtr.Equal(StrPtrLen("Basic") ) ) continue; theFullRequestPtr->ConsumeWhitespace(); theFullRequestPtr->ConsumeUntil(&strPtr, StringParser::sEOLMask); if ( strPtr.Len == 0 ) break; (void) ParseAuthNameAndPassword(&strPtr,namePtr, passwordPtr); if (namePtr->Len == 0) break; hasAuthentication = true; }; return hasAuthentication; } Bool16 Authenticate(QTSS_RTSPRequestObject request, StrPtrLen* namePtr, StrPtrLen* passwordPtr) { Bool16 authenticated = true; char* authName = namePtr->GetAsCString(); OSCharArrayDeleter authNameDeleter(authName); char* authMoviesDir = NULL; (void)QTSS_GetValueAsString(sServerPrefs, qtssPrefsMovieFolder, 0, &authMoviesDir); OSCharArrayDeleter authMovieDirDeleter(authMoviesDir); QTSS_ActionFlags authAction = qtssActionFlagsRead | qtssActionFlagsWrite; // authenticate callback to retrieve the password QTSS_Error err = QTSS_Authenticate(authName, sAuthResourceLocalPath, authMoviesDir, authAction, qtssAuthBasic, request); if (err != QTSS_NoErr) { printf("QTSSAdminModule::Authenticate: QTSS_Authenticate failed\n"); 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) { 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_StandardRTSP_Params* inParams) { Bool16 allowed = false; QTSS_RTSPRequestObject request = inParams->inRTSPRequest; // 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 & (qtssActionFlagsRead | qtssActionFlagsWrite))) return QTSS_NoErr; QTSS_UserProfileObject theUserProfile = QTSSModuleUtils::GetUserProfileObject(request); if (NULL == theUserProfile) return QTSS_RequestFailed; // This code authorizes the request if the username matches the hardcoded sAdministrator name /*char* username = QTSSModuleUtils::GetUserName_Copy(theUserProfile); OSCharArrayDeleter usernameDeleter(username); (void) QTSS_SetValue(request,qtssRTSPReqURLRealm, 0, sAuthRealm, ::strlen(sAuthRealm)); StrPtrLen usernamePtr; if(username != NULL) usernamePtr.Set(username, ::strlen(username)); if ((username== NULL) || !usernamePtr.Equal(sAdministrator)) { 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; } */ (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; } inline Bool16 AcceptSession(QTSS_RTSPSessionObject inRTSPSession) { char remoteAddress[kRemoteAddressSize] = {0}; StrPtrLen theClientIPAddressStr; theClientIPAddressStr.Set(remoteAddress,kRemoteAddressSize); QTSS_Error err = QTSS_GetValue(inRTSPSession, qtssRTSPSesRemoteAddrStr, 0, (void*)theClientIPAddressStr.Ptr, &theClientIPAddressStr.Len); if (err != QTSS_NoErr) return false; return AcceptAddress(theClientIPAddressStr.Ptr); } Bool16 StillFlushing(QTSS_Filter_Params* inParams,Bool16 flushing) { QTSS_Error err = QTSS_NoErr; if (flushing) { err = QTSS_Flush(inParams->inRTSPRequest); //printf("Flushing session=%lu QTSS_Flush err =%ld\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); //printf("Flushing session=%lu QTSS_RequestEvent err =%ld\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(); //printf("Done Flushing session=%lu\n",sSessID); return true; } } return sFlushing; } Bool16 IsAuthentic(QTSS_Filter_Params* inParams,StringParser *fullRequestPtr) { //printf("sAuthenticationEnabled \n"); Bool16 isAuthentic = false; if (!sAuthenticationEnabled) { isAuthentic = true; (void) QTSS_SetValue(inParams->inRTSPRequest, sAuthenticatedID, 0, (void*)&isAuthentic, sizeof(isAuthentic)); return isAuthentic; } StrPtrLen authenticateName; StrPtrLen authenticatePassword; Bool16 hasAuthentication = HasAuthentication(fullRequestPtr,&authenticateName,&authenticatePassword); if (hasAuthentication) { //printf("hasAuthentication\n"); isAuthentic = Authenticate(inParams->inRTSPRequest, &authenticateName,&authenticatePassword); } (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; //printf("(currentTime < nextExecuteTime) sSessID = %lu waitTime =%ld currentTime = %qd nextExecute = %qd interval=%lu\n",sSessID, waitTime, currentTime, nextExecuteTime,sRequestTimeIntervalMilli); (void)QTSS_SetIdleTimer(waitTime); KeepSession(inParams->inRTSPRequest,true); //printf("-- call me again after %ld millisecs session=%lu \n",waitTime,sSessID); return true; } sLastRequestTime = QTSS_Milliseconds(); //printf("handle sessID=%lu time=%qd \n",sSessID,currentTime); return false; } inline void GetQueryData(QTSS_RTSPRequestObject theRequest) { sAdminPtr = NEW AdminClass(); Assert(sAdminPtr != NULL); if (sAdminPtr == NULL) { //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; //printf("no flush val so set to false session=%lu err =%ld\n",sSessID, err); err =QTSS_SetValue(inParams->inRTSPRequest, sFlushingID, 0, (void*)&result, paramLen); //printf("QTSS_SetValue flush session=%lu err =%ld\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 (true == GetRequestFlushState(inParams)) { StillFlushing(inParams,true); return QTSS_NoErr; } if (!QTSS_IsGlobalLocked()) { if (InWaitInterval(inParams)) return QTSS_NoErr; //printf("New Request Wait for GlobalLock session=%lu\n",sSessID); (void)QTSS_RequestGlobalLock(); KeepSession(theRequest,true); return QTSS_NoErr; } //printf("Handle request session=%lu\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; }