/ OSX / libsecurityd / lib / SharedMemoryClient.cpp
SharedMemoryClient.cpp
  1  #include <sys/mman.h>
  2  #include <sys/types.h>
  3  #include <sys/stat.h>
  4  #include <unistd.h>
  5  #include <fcntl.h>
  6  #include <architecture/byte_order.h>
  7  #include <security_cdsa_utilities/cssmdb.h>
  8  #include "SharedMemoryClient.h"
  9  #include <string>
 10  #include <security_utilities/crc.h>
 11  #include <securityd_client/ssnotify.h>
 12  #include <Security/SecKeychain.h>
 13  
 14  using namespace Security;
 15  
 16  //=================================================================================
 17  //                          SharedMemoryClient
 18  //=================================================================================
 19  
 20  #if !defined(NDEBUG)
 21  static std::string unixerrorstr(int errnum) {
 22      string errstr;
 23      char buf[1024];
 24      // might return ERANGE
 25      /* int rx = */ strerror_r(errnum, buf, sizeof(buf));
 26      errstr = string(buf);
 27      errstr += "(" + to_string(errnum) + ")";
 28      return errstr;
 29  }
 30  #endif
 31  
 32  SharedMemoryClient::SharedMemoryClient (const char* segmentName, SegmentOffsetType segmentSize, uid_t uid)
 33  {
 34  	StLock<Mutex> _(mMutex);
 35  	
 36  	mSegmentName = segmentName;
 37  	mSegmentSize = segmentSize;
 38      mSegment = (u_int8_t*) MAP_FAILED;
 39      mDataArea = mDataPtr = 0;
 40      mUID = uid;
 41      
 42      secdebug("MDSPRIVACY","[%03d] creating SharedMemoryClient with segmentName %s, size: %d", mUID, segmentName, segmentSize);
 43  
 44      if (segmentSize < sizeof(u_int32_t))
 45  		return;
 46  
 47  	// make the name
 48  	int segmentDescriptor;
 49  	{
 50          std::string name(SharedMemoryCommon::SharedMemoryFilePath(mSegmentName.c_str(), mUID));
 51  
 52  		// make a connection to the shared memory block
 53  		segmentDescriptor = open (name.c_str(), O_RDONLY, S_IROTH);
 54  		if (segmentDescriptor < 0) // error on opening the shared memory segment?
 55  		{
 56              secdebug("MDSPRIVACY","[%03d] SharedMemoryClient open of %s failed: %s", mUID, name.c_str(), unixerrorstr(errno).c_str());
 57  			// CssmError::throwMe (CSSM_ERRCODE_INTERNAL_ERROR);
 58  			return;
 59  		}
 60  	}
 61  
 62      // check that the file size is large enough to support operations
 63      struct stat statResult = {};
 64      int result = fstat(segmentDescriptor, &statResult);
 65      if(result) {
 66          secdebug("MDSPRIVACY","[%03d] SharedMemoryClient fstat failed: %d/%s", mUID, result, unixerrorstr(errno).c_str());
 67          UnixError::throwMe(errno);
 68      }
 69  
 70      off_t sz = statResult.st_size;
 71      if(sz < sizeof(SegmentOffsetType)) {
 72          close(segmentDescriptor);
 73          return;
 74      }
 75  
 76      if(sz > 4*segmentSize) {
 77          // File is too ridiculously large. Quit.
 78          close(segmentDescriptor);
 79          return;
 80      }
 81  
 82  	// map the segment into place
 83  	mSegment = (u_int8_t*) mmap (NULL, segmentSize, PROT_READ, MAP_SHARED, segmentDescriptor, 0);
 84  	close (segmentDescriptor);
 85  
 86  	if (mSegment == MAP_FAILED)
 87  	{
 88          secdebug("MDSPRIVACY","[%03d] SharedMemoryClient mmap failed: %d", mUID, errno);
 89  		return;
 90  	}
 91  	
 92  	mDataArea = mSegment + sizeof (SegmentOffsetType);
 93  	mDataMax = mSegment + sz;
 94  	mDataPtr = mDataArea + GetProducerCount ();
 95  }
 96  
 97  
 98  
 99  SharedMemoryClient::~SharedMemoryClient ()
100  {
101      if (!uninitialized()) {
102          StLock<Mutex> _(mMutex);
103          munmap (mSegment, mSegmentSize);
104      }
105  }
106  
107  
108  SegmentOffsetType SharedMemoryClient::GetProducerCount ()
109  {
110      if (uninitialized()) {
111          secdebug("MDSPRIVACY","[%03d] SharedMemoryClient::GetProducerCount uninitialized", mUID);
112  		CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
113  	}
114      if( ((u_int8_t*) (((u_int32_t*) mSegment) + 1)) > mDataMax) {
115          // Check we can actually read this u_int32_t
116          secdebug("MDSPRIVACY","[%03d] SharedMemoryClient::GetProducerCount uint > mDataMax", mUID);
117          CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
118      }
119  
120  	SegmentOffsetType offset = OSSwapBigToHostInt32 (*(u_int32_t*) mSegment);
121      if (&mSegment[offset] >= mDataMax) {
122          secdebug("MDSPRIVACY","[%03d] SharedMemoryClient::GetProducerCount offset > mDataMax", mUID);
123  		CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
124      }
125  
126      return offset;
127  }
128  
129  void SharedMemoryClient::ReadData (void* buffer, SegmentOffsetType length)
130  {
131      if (uninitialized()) {
132          secdebug("MDSPRIVACY","[%03d] ReadData mSegment fail uninitialized: %p", mUID, mSegment);
133          CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
134      }
135      
136  	u_int8_t* bptr = (u_int8_t*) buffer;
137  
138  	SegmentOffsetType bytesToEnd = (SegmentOffsetType)(mDataMax - mDataPtr);
139  	
140  	// figure out how many bytes we can read
141  	SegmentOffsetType bytesToRead = (length <= bytesToEnd) ? length : bytesToEnd;
142  
143  	// move the first part of the data
144  	memcpy (bptr, mDataPtr, bytesToRead);
145  	bptr += bytesToRead;
146  	
147  	// see if we have anything else to read
148  	mDataPtr += bytesToRead;
149  	
150  	length -= bytesToRead;
151  	if (length != 0)
152  	{
153  		mDataPtr = mDataArea;
154  		memcpy(bptr, mDataPtr, length);
155  		mDataPtr += length;
156  	}
157  }
158  
159  
160  
161  SegmentOffsetType SharedMemoryClient::ReadOffset()
162  {
163  	SegmentOffsetType offset;
164  	ReadData(&offset, sizeof(SegmentOffsetType));
165  	offset = OSSwapBigToHostInt32 (offset);
166  	return offset;
167  }
168  
169  
170  
171  bool SharedMemoryClient::ReadMessage (void* message, SegmentOffsetType &length, UnavailableReason &ur)
172  {
173  	StLock<Mutex> _(mMutex);
174  
175      if (uninitialized()) {
176  		secdebug("MDSPRIVACY","[%03d] ReadMessage mSegment fail uninitialized: %p", mUID, mSegment);
177  		CssmError::throwMe(CSSM_ERRCODE_INTERNAL_ERROR);
178  	}
179  
180  	ur = kURNone;
181  	
182  	size_t offset = mDataPtr - mDataArea;
183  	if (offset == GetProducerCount())
184  	{
185          secdebug("MDSPRIVACY","[%03d] ReadMessage GetProducerCount()", mUID);
186  		ur = kURNoMessage;
187  		return false;
188  	}
189  	
190  	// get the length of the message in the buffer
191  	length = ReadOffset();
192  	
193  	// we have the possibility that data is correct, figure out where the data is actually located
194  	// get the length of the message stored there
195  	if (length == 0 || length >= kPoolAvailableForData)
196  	{
197          secdebug("MDSPRIVACY","[%03d] ReadMessage length error: %d", mUID, length);
198          ur = (length == 0) ? kURNoMessage : kURBufferCorrupt;
199  
200  		// something's gone wrong, reset.
201  		mDataPtr = mDataArea + GetProducerCount ();
202  		return false;
203  	}
204  	
205  	// read the crc
206  	SegmentOffsetType crc = ReadOffset();
207  
208  	// read the data into the buffer
209  	ReadData (message, length);
210  	
211  	// calculate the CRC
212  	SegmentOffsetType crc2 = CalculateCRC((u_int8_t*) message, length);
213  	if (crc != crc2)
214  	{
215  		ur = kURBufferCorrupt;
216  		mDataPtr = mDataArea + GetProducerCount ();
217  		return false;
218  	}
219  
220  	return true;
221  }
222  
223  //=================================================================================
224  //                          SharedMemoryCommon
225  //=================================================================================
226  
227  std::string SharedMemoryCommon::SharedMemoryFilePath(const char *segmentName, uid_t uid) {
228      std::string path;
229      uid = SharedMemoryCommon::fixUID(uid);
230      path = SharedMemoryCommon::kMDSMessagesDirectory;   // i.e. /private/var/db/mds/messages/
231      if (uid != 0) {
232          path += std::to_string(uid) + "/";              // e.g. /private/var/db/mds/messages/501/
233      }
234  
235      path += SharedMemoryCommon::kUserPrefix;            // e.g. /var/db/mds/messages/se_
236      path += segmentName;                                // e.g. /var/db/mds/messages/501/se_SecurityMessages
237      return path;
238  }
239  
240  std::string SharedMemoryCommon::notificationDescription(int domain, int event) {
241      string domainstr, eventstr;
242  
243      switch (domain) {
244          case Security::SecurityServer::kNotificationDomainAll:        domainstr = "all";      break;
245          case Security::SecurityServer::kNotificationDomainDatabase:   domainstr = "database"; break;
246          case Security::SecurityServer::kNotificationDomainPCSC:       domainstr = "pcsc";     break;
247          case Security::SecurityServer::kNotificationDomainCDSA:       domainstr = "CDSA";     break;
248          default:
249              domainstr = "unknown";
250              break;
251      }
252  
253      switch (event) {
254          case kSecLockEvent:                 eventstr = "lock";              break;
255          case kSecUnlockEvent:               eventstr = "unlock";            break;
256          case kSecAddEvent:                  eventstr = "add";               break;
257          case kSecDeleteEvent:               eventstr = "delete";            break;
258          case kSecUpdateEvent:               eventstr = "update";            break;
259          case kSecPasswordChangedEvent:      eventstr = "passwordChange";    break;
260          case kSecDefaultChangedEvent:       eventstr = "defaultChange";     break;
261          case kSecDataAccessEvent:           eventstr = "dataAccess";        break;
262          case kSecKeychainListChangedEvent:  eventstr = "listChange";        break;
263          case kSecTrustSettingsChangedEvent: eventstr = "trustSettings";     break;
264          default:
265              domainstr = "unknown";
266              break;
267      }
268  
269      return "Domain: " + domainstr + ", Event: " + eventstr;
270  }