2 * Copyright (c) 2016-2017 Apple Inc. All Rights Reserved.
4 * @APPLE_LICENSE_HEADER_START@
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
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.
21 * @APPLE_LICENSE_HEADER_END@
24 #include "SharedMemoryServer.h"
27 #include <sys/errno.h>
29 #include <machine/byte_order.h>
32 #include <security_utilities/crc.h>
33 #include <security_utilities/casts.h>
38 Logically, these should go in /var/run/mds, but we know that /var/db/mds
39 already exists at install time.
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
) {
47 secdebug("MDSPRIVACY","Failed to make directory: %s (%d)", path
, errno
);
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
);
59 SharedMemoryServer::SharedMemoryServer (const char* segmentName
, SegmentOffsetType segmentSize
, uid_t uid
, gid_t gid
) :
60 mSegmentName (segmentName
), mSegmentSize (segmentSize
), mUID(SharedMemoryCommon::fixUID(uid
))
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
);
66 // make the mds directory, just in case it doesn't exist
68 makedir(SharedMemoryCommon::kMDSDirectory
, perm1777
);
69 makedir(SharedMemoryCommon::kMDSMessagesDirectory
, perm0755
);
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
);
77 mFileName
= SharedMemoryCommon::SharedMemoryFilePath(segmentName
, uid
);
80 // clean any old file away
81 unlinkfile(mFileName
.c_str());
84 secdebug("MDSPRIVACY","creating %s",mFileName
.c_str ());
86 mBackingFile
= open (mFileName
.c_str (), O_RDWR
| O_CREAT
| O_EXCL
, perm0600
);
89 mBackingFile
= open (mFileName
.c_str (), O_RDWR
| O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
94 secdebug("MDSPRIVACY","creation of %s failed", mFileName
.c_str());
98 int rx
= fchown(mBackingFile
, uid
, gid
);
100 secdebug("MDSPRIVACY","chown of %s to %d/%d failed : %d", mFileName
.c_str(), uid
, gid
, rx
);
103 // set the segment size
104 ftruncate (mBackingFile
, segmentSize
);
106 // map it into memory
107 mSegment
= (u_int8_t
*) mmap (NULL
, mSegmentSize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, mBackingFile
, 0);
109 if (mSegment
== MAP_FAILED
) // can't map the memory?
112 unlinkfile(mFileName
.c_str());
114 mDataPtr
= mDataArea
= mSegment
+ sizeof(SegmentOffsetType
);
115 mDataMax
= mSegment
+ segmentSize
;;
117 SetProducerOffset (0);
121 SharedMemoryServer::~SharedMemoryServer ()
124 if (mSegment
== NULL
)
130 munmap (mSegment
, mSegmentSize
);
134 // mark the segment for deletion
135 unlinkfile(mFileName
.c_str ());
140 const SegmentOffsetType
142 kCRCOffset
= kSegmentLength
+ sizeof(SegmentOffsetType
),
143 kDomainOffset
= kCRCOffset
+ sizeof(SegmentOffsetType
),
144 kEventTypeOffset
= kDomainOffset
+ sizeof(SegmentOffsetType
),
145 kHeaderLength
= kEventTypeOffset
+ sizeof(SegmentOffsetType
) - kCRCOffset
;
147 void SharedMemoryServer::WriteMessage (SegmentOffsetType domain
, SegmentOffsetType event
, const void *message
, SegmentOffsetType messageLength
)
149 // backing file MUST be right size, don't ftruncate() more then needed though to avoid reaching too deep into filesystem
151 if (::fstat(mBackingFile
, &sb
) == 0 && sb
.st_size
!= (off_t
)mSegmentSize
) {
152 ::ftruncate(mBackingFile
, mSegmentSize
);
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
);
163 SegmentOffsetType crc
= CalculateCRC(finalMessage
.data(), messageSize
);
166 WriteOffset(int_cast
<size_t, SegmentOffsetType
>(messageSize
));
172 WriteData (finalMessage
.data(), int_cast
<size_t, SegmentOffsetType
>(messageSize
));
174 // write the data count
175 SetProducerOffset(int_cast
<size_t, SegmentOffsetType
>(mDataPtr
- mDataArea
));
180 const char* SharedMemoryServer::GetSegmentName ()
182 return mSegmentName
.c_str ();
187 size_t SharedMemoryServer::GetSegmentSize ()
193 void SharedMemoryServer::SetProducerOffset (SegmentOffsetType producerCount
)
195 *((SegmentOffsetType
*) mSegment
) = OSSwapHostToBigInt32 (producerCount
);
200 void SharedMemoryServer::WriteOffset(SegmentOffsetType offset
)
203 *((u_int32_t
*) buffer
) = OSSwapHostToBigInt32(offset
);
204 WriteData(buffer
, 4);
209 void SharedMemoryServer::WriteData(const void* data
, SegmentOffsetType length
)
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
);
216 // figure out how many bytes we can write
217 SegmentOffsetType bytesToWrite
= (length
<= bytesToEnd
) ? length
: bytesToEnd
;
219 // move the first part of the data, making sure to skip the producer pointer
220 memcpy (mDataPtr
, dp
, bytesToWrite
);
221 mDataPtr
+= bytesToWrite
;
224 // deduct the bytes just written
225 length
-= bytesToWrite
;
227 if (length
!= 0) // did we wrap around?
229 mDataPtr
= mDataArea
;
230 memcpy (mDataPtr
, dp
, length
);