/ securityd / src / SharedMemoryServer.cpp
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  }