SharedMemoryServer.cpp
1 /* 2 * Copyright (c) 2016-2017 Apple Inc. All Rights Reserved. 3 * 4 * @APPLE_LICENSE_HEADER_START@ 5 * 6 * This file contains Original Code and/or Modifications of Original Code 7 * as defined in and that are subject to the Apple Public Source License 8 * Version 2.0 (the 'License'). You may not use this file except in 9 * compliance with the License. Please obtain a copy of the License at 10 * http://www.opensource.apple.com/apsl/ and read it before using this 11 * file. 12 * 13 * The Original Code and all software distributed under the License are 14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER 15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES, 16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY, 17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT. 18 * Please see the License for the specific language governing rights and 19 * limitations under the License. 20 * 21 * @APPLE_LICENSE_HEADER_END@ 22 */ 23 24 #include "SharedMemoryServer.h" 25 #include <stdlib.h> 26 #include <sys/mman.h> 27 #include <sys/errno.h> 28 #include <fcntl.h> 29 #include <machine/byte_order.h> 30 #include <string> 31 #include <sys/stat.h> 32 #include <security_utilities/crc.h> 33 #include <security_utilities/casts.h> 34 #include <unistd.h> 35 #include <vector> 36 37 /* 38 Logically, these should go in /var/run/mds, but we know that /var/db/mds 39 already exists at install time. 40 */ 41 42 static bool makedir(const char *path, mode_t mode) { 43 // Returns true on success. Primarily to centralize logging 44 if (::mkdir(path, mode)==0 || errno==EEXIST) { 45 return true; 46 } else { 47 secdebug("MDSPRIVACY","Failed to make directory: %s (%d)", path, errno); 48 return false; 49 } 50 } 51 52 static void unlinkfile(const char *path) { 53 // Primarily to centralize logging 54 if (::unlink(path)==-1) { 55 secdebug("MDSPRIVACY","Failed to unlink file: %s (%d)", path, errno); 56 } 57 } 58 59 SharedMemoryServer::SharedMemoryServer (const char* segmentName, SegmentOffsetType segmentSize, uid_t uid, gid_t gid) : 60 mSegmentName (segmentName), mSegmentSize (segmentSize), mUID(SharedMemoryCommon::fixUID(uid)) 61 { 62 const mode_t perm1777 = S_ISVTX | S_IRWXU | S_IRWXG | S_IRWXO; 63 const mode_t perm0755 = S_IRWXU | (S_IRGRP | S_IXGRP) | (S_IROTH | S_IXOTH); 64 const mode_t perm0600 = (S_IRUSR | S_IWUSR); 65 66 // make the mds directory, just in case it doesn't exist 67 if (mUID == 0) { 68 makedir(SharedMemoryCommon::kMDSDirectory, perm1777); 69 makedir(SharedMemoryCommon::kMDSMessagesDirectory, perm0755); 70 } else { 71 // Assume kMDSMessagesDirectory was created first by securityd 72 std::string uidstr = std::to_string(mUID); 73 std::string upath = SharedMemoryCommon::kMDSMessagesDirectory; 74 upath += "/" + uidstr; 75 makedir(upath.c_str(), perm0755); 76 } 77 mFileName = SharedMemoryCommon::SharedMemoryFilePath(segmentName, uid); 78 79 // make the file name 80 // clean any old file away 81 unlinkfile(mFileName.c_str()); 82 83 // open the file 84 secdebug("MDSPRIVACY","creating %s",mFileName.c_str ()); 85 if(mUID != 0) { 86 mBackingFile = open (mFileName.c_str (), O_RDWR | O_CREAT | O_EXCL, perm0600); 87 } 88 else { 89 mBackingFile = open (mFileName.c_str (), O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 90 } 91 92 if (mBackingFile < 0) 93 { 94 secdebug("MDSPRIVACY","creation of %s failed", mFileName.c_str()); 95 return; 96 } 97 98 int rx = fchown(mBackingFile, uid, gid); 99 if (rx) { 100 secdebug("MDSPRIVACY","chown of %s to %d/%d failed : %d", mFileName.c_str(), uid, gid, rx); 101 } 102 103 // set the segment size 104 ftruncate (mBackingFile, segmentSize); 105 106 // map it into memory 107 mSegment = (u_int8_t*) mmap (NULL, mSegmentSize, PROT_READ | PROT_WRITE, MAP_SHARED, mBackingFile, 0); 108 109 if (mSegment == MAP_FAILED) // can't map the memory? 110 { 111 mSegment = NULL; 112 unlinkfile(mFileName.c_str()); 113 } else { 114 mDataPtr = mDataArea = mSegment + sizeof(SegmentOffsetType); 115 mDataMax = mSegment + segmentSize;; 116 117 SetProducerOffset (0); 118 } 119 } 120 121 SharedMemoryServer::~SharedMemoryServer () 122 { 123 // go away 124 if (mSegment == NULL) 125 { 126 return; 127 } 128 129 // get out of memory 130 munmap (mSegment, mSegmentSize); 131 132 close(mBackingFile); 133 134 // mark the segment for deletion 135 unlinkfile(mFileName.c_str ()); 136 } 137 138 139 140 const SegmentOffsetType 141 kSegmentLength = 0, 142 kCRCOffset = kSegmentLength + sizeof(SegmentOffsetType), 143 kDomainOffset = kCRCOffset + sizeof(SegmentOffsetType), 144 kEventTypeOffset = kDomainOffset + sizeof(SegmentOffsetType), 145 kHeaderLength = kEventTypeOffset + sizeof(SegmentOffsetType) - kCRCOffset; 146 147 void SharedMemoryServer::WriteMessage (SegmentOffsetType domain, SegmentOffsetType event, const void *message, SegmentOffsetType messageLength) 148 { 149 // backing file MUST be right size, don't ftruncate() more then needed though to avoid reaching too deep into filesystem 150 struct stat sb; 151 if (::fstat(mBackingFile, &sb) == 0 && sb.st_size != (off_t)mSegmentSize) { 152 ::ftruncate(mBackingFile, mSegmentSize); 153 } 154 155 // assemble the final message 156 ssize_t messageSize = kHeaderLength + messageLength; 157 std::vector<u_int8_t> finalMessage(messageSize); 158 SegmentOffsetType *fm = (SegmentOffsetType*) finalMessage.data(); 159 fm[0] = OSSwapHostToBigInt32(domain); 160 fm[1] = OSSwapHostToBigInt32(event); 161 memcpy(&fm[2], message, messageLength); 162 163 SegmentOffsetType crc = CalculateCRC(finalMessage.data(), messageSize); 164 165 // write the length 166 WriteOffset(int_cast<size_t, SegmentOffsetType>(messageSize)); 167 168 // write the crc 169 WriteOffset(crc); 170 171 // write the data 172 WriteData (finalMessage.data(), int_cast<size_t, SegmentOffsetType>(messageSize)); 173 174 // write the data count 175 SetProducerOffset(int_cast<size_t, SegmentOffsetType>(mDataPtr - mDataArea)); 176 } 177 178 179 180 const char* SharedMemoryServer::GetSegmentName () 181 { 182 return mSegmentName.c_str (); 183 } 184 185 186 187 size_t SharedMemoryServer::GetSegmentSize () 188 { 189 return mSegmentSize; 190 } 191 192 193 void SharedMemoryServer::SetProducerOffset (SegmentOffsetType producerCount) 194 { 195 *((SegmentOffsetType*) mSegment) = OSSwapHostToBigInt32 (producerCount); 196 } 197 198 199 200 void SharedMemoryServer::WriteOffset(SegmentOffsetType offset) 201 { 202 u_int8_t buffer[4]; 203 *((u_int32_t*) buffer) = OSSwapHostToBigInt32(offset); 204 WriteData(buffer, 4); 205 } 206 207 208 209 void SharedMemoryServer::WriteData(const void* data, SegmentOffsetType length) 210 { 211 // figure out where in the buffer we actually need to write the data 212 // figure out how many bytes we can write without overflowing the buffer 213 const u_int8_t* dp = (const u_int8_t*) data; 214 SegmentOffsetType bytesToEnd = int_cast<ptrdiff_t, SegmentOffsetType>(mDataMax - mDataPtr); 215 216 // figure out how many bytes we can write 217 SegmentOffsetType bytesToWrite = (length <= bytesToEnd) ? length : bytesToEnd; 218 219 // move the first part of the data, making sure to skip the producer pointer 220 memcpy (mDataPtr, dp, bytesToWrite); 221 mDataPtr += bytesToWrite; 222 dp += bytesToWrite; 223 224 // deduct the bytes just written 225 length -= bytesToWrite; 226 227 if (length != 0) // did we wrap around? 228 { 229 mDataPtr = mDataArea; 230 memcpy (mDataPtr, dp, length); 231 mDataPtr += length; 232 } 233 }