Darwin-Streaming-Server/APIModules/QTSSFilePrivsModule.bproj/QTSSFilePrivsModule.cpp
Darren VanBuren 849723c9cf Add even more of the source
This should be about everything needed to build so far?
2017-03-07 17:14:16 -08:00

738 lines
22 KiB
C++

/*
*
* @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: QTSSFilePrivsModule.cpp
Contains: Implementation of QTSSFilePrivsModule.
*/
#include "QTSSFilePrivsModule.h"
#include "OSArrayObjectDeleter.h"
#include "QTSS_Private.h"
#include "StrPtrLen.h"
#include "OSMemory.h"
#include "MyAssert.h"
#include "StringFormatter.h"
#include "StrPtrLen.h"
#include "StringParser.h"
#include "QTSSModuleUtils.h"
#include "base64.h"
#include "OS.h"
#ifndef __MW_
#include <sys/errno.h>
#include <pwd.h>
#include <grp.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#endif
// STATIC DATA
static StrPtrLen sDefaultRealm("WWW Streaming Server"); // testing only
static StrPtrLen sSDPSuffix(".sdp");
static char* sRootUserPtr = "root";
const UInt32 kMaxPathLen = 512;
static OSMutex* sUserMutex = NULL;
// FUNCTION PROTOTYPES
QTSS_Error QTSSFilePrivsModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams);
QTSS_Error Register(QTSS_Register_Params* inParams);
QTSS_Error Initialize(QTSS_Initialize_Params* inParams);
QTSS_Error Shutdown();
QTSS_Error RereadPrefs();
QTSS_Error AuthenticateRTSPRequest(QTSS_StandardRTSP_Params* inParams);
Bool16 QTSSAuthorize(QTSS_StandardRTSP_Params* inParams, const char* pathBuff);
Bool16 CheckWorldAccess(const char* pathBuff);
Bool16 CheckPassword(QTSS_StandardRTSP_Params* inParams);
Bool16 FileExists(char* pathBuff);
UInt32 GetPathParentDestructive(const StrPtrLen *thePathPtr, StrPtrLen *resultPathPtr, UInt32 maxLen);
// FUNCTION IMPLEMENTATIONS
QTSS_Error QTSSFilePrivsModule_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, QTSSFilePrivsModuleDispatch);
}
QTSS_Error QTSSFilePrivsModuleDispatch(QTSS_Role inRole, QTSS_RoleParamPtr inParams)
{
switch (inRole)
{
case QTSS_Register_Role:
return Register(&inParams->regParams);
break;
case QTSS_Initialize_Role:
return Initialize(&inParams->initParams);
break;
case QTSS_RereadPrefs_Role:
return RereadPrefs();
break;
case QTSS_RTSPAuthorize_Role:
return AuthenticateRTSPRequest(&inParams->rtspRequestParams);
break;
case QTSS_Shutdown_Role:
return Shutdown();
break;
}
return QTSS_NoErr;
}
QTSS_Error Register(QTSS_Register_Params* inParams)
{
// Do role & attribute setup
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RereadPrefs_Role);
(void)QTSS_AddRole(QTSS_RTSPAuthorize_Role);
// Tell the server our name!
static char* sModuleName = "QTSSFilePrivsModule";
::strcpy(inParams->outModuleName, sModuleName);
return QTSS_NoErr;
}
QTSS_Error Initialize(QTSS_Initialize_Params* inParams)
{
// Setup module utils
QTSSModuleUtils::Initialize(inParams->inMessages, inParams->inServer, inParams->inErrorLogStream);
sUserMutex = NEW OSMutex();
RereadPrefs();
return QTSS_NoErr;
}
QTSS_Error Shutdown()
{
return QTSS_NoErr;
}
QTSS_Error RereadPrefs()
{
return QTSS_NoErr;
}
UInt32 GetPathParentDestructive(const StrPtrLen *thePathPtr, StrPtrLen *resultPathPtr, UInt32 maxLen)
{
if (resultPathPtr && thePathPtr)
{
StrPtrLen thePath = *thePathPtr;
while ( (thePath.Len > 0) && (thePath.Ptr[thePath.Len -1] == '/') )
{
thePath.Len--;
}
while ( (thePath.Len > 0) && (thePath.Ptr[thePath.Len -1] != '/') )
{
thePath.Len--;
}
if (thePath.Len < maxLen)
{
memcpy (resultPathPtr->Ptr,thePath.Ptr, thePath.Len);
resultPathPtr->Len = thePath.Len;
resultPathPtr->Ptr[thePath.Len] = 0;
//qtss_printf("new dir =%s \n",resultPathPtr->Ptr);
}
}
return resultPathPtr->Len;
}
Bool16 IsUserMember(uid_t userID, gid_t groupID)
{
struct passwd* user = getpwuid(userID);
struct group* group = getgrgid(groupID);
if ((user == NULL) || (group == NULL))
return false;
if (user->pw_gid == groupID) return true;
int i = 0;
while (group->gr_mem[i] != NULL)
{
if (!strcmp(user->pw_name, group->gr_mem[i]))
return true;
i++;
}
return false;
}
Bool16 CheckFileAccess(struct passwd *passwdStructPtr, StrPtrLen */*nameStrPtr*/, char* pathBuff)
{
Bool16 result = false;
/*
if the user is the owner then
if owner access then succeed
else fail
if user is in group then
if group access then succeed
else fail
if guest is on then
succed
else fail
*/
do // once only check for the current entity
{
struct stat statData;
int statResult = stat(pathBuff,&statData);
if (0 != statResult)
{
//qtss_printf("no access to file =%s\n",pathBuff);
result = true; // let access error be handled by server
break;
}
if ( statData.st_uid == (UInt16) passwdStructPtr->pw_uid) // check if owner
{
if ( (statData.st_mode & 0400 ) != 0 ) // has owner read access
{ //qtss_printf("owner access to file =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("no owner access to file =%s\n",pathBuff);
result = false;
break;
}
}
if (statData.st_gid == (UInt16) passwdStructPtr->pw_gid) // check if user's default group owns
{
if ( (statData.st_mode & 0040) != 0 ) // has group read access
{ //qtss_printf("user default group has access to file =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("user default group has no access to file =%s\n",pathBuff);
result = false;
break;
}
}
if (IsUserMember(passwdStructPtr->pw_uid, statData.st_gid)) // check if user in group
{
if ( (statData.st_mode & 0040) != 0 ) // has group read access
{ //qtss_printf("user member group has access to file =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("user member group has no access to file =%s\n",pathBuff);
result = false;
break;
}
}
if ( (statData.st_mode & 0004) != 0 ) // has world read access
{
//qtss_printf("world has access to file =%s\n",pathBuff);
result = true;
break;
}
//qtss_printf("world has no read access to file =%s\n",pathBuff);
result = false;
} while (false);
if (!result)
{
//qtss_printf("CheckFileAccess failed for %s on file %s\n",nameStrPtr->Ptr, pathBuff);
}
else
{
//qtss_printf("success on file %s for %s\n",pathBuff, nameStrPtr->Ptr);
}
return result;
}
Bool16 CheckDirAccess(struct passwd *passwdStructPtr, StrPtrLen */*nameStrPtr*/, char* pathBuff)
{
Bool16 result = true;
char searchBuffer[kMaxPathLen] = {};
StrPtrLen searchPath(searchBuffer, kMaxPathLen -1);
StrPtrLen thePath(pathBuff);
/*
for each directory in path until fail
if the user is the owner then
if owner access then succeed-continue
else fail - stop
if user is in group then
if group access then succeed-continue
else fail - stop
if guest access then
succeed - continue
else fail - stop
*/
if (thePath.Len <= searchPath.Len)
{
memcpy(searchBuffer,thePath.Ptr, thePath.Len);
while ( (true == result) && (0 != GetPathParentDestructive(&searchPath, &searchPath, kMaxPathLen)) ) // loop until fail or have checked full directory path and file
{
result = false;
do // once only check for the current entity
{
struct stat statData;
int statResult = stat(searchBuffer,&statData);
if (0 != statResult)
{
//qtss_printf("no access to path =%s\n",searchBuffer);
result = true; // let the error be handled in the server
break; // allow
}
if ( statData.st_uid == (UInt16) passwdStructPtr->pw_uid) // check if owner
{
if ( (statData.st_mode & 0100 ) != 0 ) // has owner search access
{ //qtss_printf("owner search access to directory =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("no owner search access to directory =%s\n",pathBuff);
result = false;
break;
}
}
if (statData.st_gid == (UInt16) passwdStructPtr->pw_gid) // check if user's default group owns
{
if ( (statData.st_mode & 0010) != 0 ) // has group search access
{ //qtss_printf("user default group has search access to directory =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("user default group has no search access to directory =%s\n",pathBuff);
result = false;
break;
}
}
if (IsUserMember(passwdStructPtr->pw_uid, statData.st_gid)) // check if user in group
{
if ( (statData.st_mode & 0010) != 0 ) // has group search access
{ //qtss_printf("user member group has search access to directory =%s\n",pathBuff);
result = true;
break;
}
else
{ //qtss_printf("user member group has no search access to directory =%s\n",pathBuff);
result = false;
break;
}
}
if ( (statData.st_mode & 0001) != 0 ) // has world search access
{
//qtss_printf("world has search access to directory =%s\n",pathBuff);
result = true;
break;
}
//qtss_printf("world has no search access to directory =%s\n",pathBuff);
result = false;
} while (false);
}
}
if (!result)
{
//qtss_printf("CheckDirAccess failed for %s on file %s\n",nameStrPtr->Ptr, searchBuffer);
}
else
{
//qtss_printf("success on file %s for %s\n",searchBuffer, nameStrPtr->Ptr);
}
return result;
}
Bool16 FileExists(char* pathBuff)
{
struct stat statData;
Bool16 result = true;
if (0 != stat(pathBuff,&statData) ) // something wrong
{
if ( OSThread::GetErrno() == ENOENT ) // doesn't exist
result = false;
}
return result;
}
Bool16 CheckWorldFileAccess(char* pathBuff)
{
Bool16 result = false;
struct stat statData;
do // once only check for the current entity
{
//qtss_printf("stat on %s \n",pathBuff);
if (0 != stat(pathBuff,&statData))
{
//qtss_printf("no access to path =%s\n",pathBuff);
result = true; // let the server deal with this one
break;
}
//qtss_printf("statData.st_mode = %x \n",statData.st_mode);
if (0 == (statData.st_mode & 0004) ) // world read access
{
//qtss_printf("no world access to path =%s\n",pathBuff);
break;
}
result = true;
} while (false);
return result;
}
Bool16 CheckWorldDirAccess(char* pathBuff)
{
Bool16 result = true;
struct stat statData;
char searchBuffer[kMaxPathLen] = {};
StrPtrLen searchPath(searchBuffer, kMaxPathLen -1);
StrPtrLen thePath(pathBuff);
if (thePath.Len <= searchPath.Len)
{
memcpy(searchBuffer,thePath.Ptr, thePath.Len);
while ( (true == result) && (0 != GetPathParentDestructive(&searchPath, &searchPath, kMaxPathLen)) ) // loop until fail or have checked full directory path and file
{
result = false;
do // once only check for the current entity
{
//qtss_printf("stat on %s \n",searchBuffer);
if (0 != stat(searchBuffer,&statData))
{
//qtss_printf("no world access to path =%s\n",searchBuffer);
result = true; // let the server deal with this one
break;
}
//qtss_printf("statData.st_mode = %x \n",statData.st_mode);
if (0 == (statData.st_mode & 0001) )
{
//qtss_printf("no world access to path =%s\n",searchBuffer);
result = false;
break;
}
result = true;
} while (false);
} ;
}
return result;
}
Bool16 QTSSAuthorize(QTSS_StandardRTSP_Params* inParams, char* pathBuff)
{
QTSS_Error theErr = QTSS_NoErr;
Bool16 result = false;
const int kBuffLen = 256;
char passwordBuff[kBuffLen] = {};
char nameBuff[kBuffLen] = {};
StrPtrLen nameStr(nameBuff, kBuffLen -1);
StrPtrLen passwordStr(passwordBuff, kBuffLen -1);
Bool16 noUserName = false;
Bool16 noPassword = false;
Bool16 isSpecialGuest = false;
do
{
theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserName,0, (void *) nameStr.Ptr, &nameStr.Len);
//qtss_printf("GetValue qtssRTSPReqUserName err =%"_S32BITARG_" \n",theErr);
if ( (theErr != QTSS_NoErr) || (nameStr.Len == 0) || (nameStr.Ptr == NULL) || (*nameStr.Ptr == '\0'))
{
//qtss_printf ("no user name\n");
noUserName = true;
}
//qtss_printf("RTSPRequest dictionary name =%s len = %"_S32BITARG_"\n",nameStr.Ptr, nameStr.Len);
theErr = QTSS_GetValue (inParams->inRTSPRequest,qtssRTSPReqUserPassword,0, (void *) passwordStr.Ptr, &passwordStr.Len);
//qtss_printf("GetValue qtssRTSPReqUserName err =%"_S32BITARG_" \n",theErr);
if ( (theErr != QTSS_NoErr) || (passwordStr.Len == 0) || (passwordStr.Ptr == NULL) || (*passwordStr.Ptr == '\0'))
{
//qtss_printf ("no Password\n");
noPassword = true;
}
//qtss_printf("RTSPRequest dictionary password =%s len = %"_S32BITARG_" \n",passwordStr.Ptr, passwordStr.Len);
if (noUserName && noPassword) isSpecialGuest = true;
if (isSpecialGuest) // no name and no password means guest
{
//qtss_printf ("User is guest check for access\n");
result = CheckWorldDirAccess(pathBuff);
if (true == result)
result = CheckWorldFileAccess(pathBuff);
break; // no further processing on guest
}
if (0 == strcasecmp(nameStr.Ptr, sRootUserPtr) )
{ //qtss_printf("user is root no root access to file =%s\n",pathBuff); // must log
result = false; // don't allow
break;
}
struct passwd *passwdStructPtr = getpwnam(nameStr.Ptr);
if (NULL == passwdStructPtr)
{
//qtss_printf("failed to find name =%s\n",passwordStr.Ptr);
break;
}
char *theCryptedPassword = crypt(passwordStr.Ptr, passwdStructPtr->pw_passwd);
if ( 0 != strcmp(passwdStructPtr->pw_passwd, theCryptedPassword ) )
{
//qtss_printf("failed to match name to password =%s\n",passwordStr.Ptr);
break;
}
result = CheckDirAccess(passwdStructPtr, &nameStr, pathBuff);
if (!result) break;
result = CheckFileAccess(passwdStructPtr, &nameStr, pathBuff);
if (!result) break;
//qtss_printf("QTSSAuthorize: user %s is authorized for %s\n",nameStr.Ptr,pathBuff);
} while (false);
if (!result)
{ //qtss_printf("QTSSAuthorize: user %s is un authorized for %s\n",nameStr.Ptr,pathBuff);
}
return result;
}
Bool16 FileOrSDPFileExists(char *pathBuff, UInt32 *pathLen, const UInt32 maxLen, QTSS_Error *theErrPtr)
{
Bool16 result = false;
do
{
if (FileExists(pathBuff))
{
//qtss_printf("file exists =%s\n",pathBuff);
result = true;
break; // file is there
}
if ( (*pathLen + sSDPSuffix.Len + 1) >= maxLen)
{
//qtss_printf("buffer too small for path\n");
if (theErrPtr) *theErrPtr = ENAMETOOLONG; // don't allow request to succed we can't authorize
break;
}
strcat(pathBuff, sSDPSuffix.Ptr);
//qtss_printf("sdp path =%s\n",pathBuff);
if (FileExists(pathBuff))
{
//qtss_printf("sdp file exists =%s\n",pathBuff);
result = true;
break; // sdp file is there
}
} while (false);
return result;
}
QTSS_Error AuthenticateRTSPRequest(QTSS_StandardRTSP_Params* inParams)
{
QTSS_Error theErr = QTSS_NoErr;
OSMutexLocker locker(sUserMutex);
/*
Notes:
pathBuff is cleared so Getting the path should leave a zero terminated string.
Order is important.
1) Get the path
2) See if the path exists or an sdp path based on the path exists
3) if world access on the path is available, allow the request
4) if non-world access on the path or file check the name and password on the path and file
*/
do
{
if ( (NULL == inParams) || (NULL == inParams->inRTSPRequest) )
{
theErr = QTSS_RequestFailed;
break;
}
UInt32 pathLen = kMaxPathLen -1;
char pathBuff[kMaxPathLen] = {};
QTSS_RTSPRequestObject theRTSPRequest = inParams->inRTSPRequest;
theErr = QTSS_GetValue (theRTSPRequest,qtssRTSPReqLocalPath,0, (void *) pathBuff, &pathLen);
if ( (theErr != QTSS_NoErr) || (0 == pathLen) )
{
//qtss_printf("path not found in request\n");
break; // Bail on the request. The Server will handle the error
}
Bool16 fileOk = FileOrSDPFileExists(pathBuff, &pathLen, kMaxPathLen, &theErr);
if (!fileOk)
{
//qtss_printf("file not found\n");
break; // Do nothing. The Server will handle the error
}
Bool16 allowRequest = QTSSAuthorize(inParams, pathBuff);
if (allowRequest)
{
//qtss_printf("user is authorized\n");
break; // Do nothing. Allow the request (the default behavior)
}
// We are not allowing the request so pass false back to the server.
theErr = QTSS_SetValue(theRTSPRequest,qtssRTSPReqUserAllowed, 0, &allowRequest, sizeof(allowRequest));
if (theErr != QTSS_NoErr) break;
#if 0 // test setting a specific realm this is not used in this module
theErr = QTSS_SetValue(theRTSPRequest,qtssRTSPReqURLRealm, 0, sDefaultRealm.Ptr, sDefaultRealm.Len);
if (theErr != QTSS_NoErr) break;
#endif
} while (false);
if (theErr)
{
Assert(0);
theErr = QTSS_RequestFailed;
}
return theErr;
}