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>
37 Logically, these should go in /var/run/mds, but we know that /var/db/mds
38 already exists at install time.
41 std::string
SharedMemoryCommon::SharedMemoryFilePath(const char *segmentName
, uid_t uid
) {
43 uid
= SharedMemoryCommon::fixUID(uid
);
44 path
= SharedMemoryCommon::kMDSMessagesDirectory
; // i.e. /private/var/db/mds/messages/
46 path
+= std::to_string(uid
) + "/"; // e.g. /private/var/db/mds/messages/501/
49 path
+= SharedMemoryCommon::kUserPrefix
; // e.g. /var/db/mds/messages/se_
50 path
+= segmentName
; // e.g. /var/db/mds/messages/501/se_SecurityMessages
54 static bool makedir(const char *path
, mode_t mode
) {
55 // Returns true on success. Primarily to centralize logging
56 if (::mkdir(path
, mode
)==0 || errno
==EEXIST
) {
59 secdebug("MDSPRIVACY","Failed to make directory: %s (%d)", path
, errno
);
64 static void unlinkfile(const char *path
) {
65 // Primarily to centralize logging
66 if (::unlink(path
)==-1) {
67 secdebug("MDSPRIVACY","Failed to unlink file: %s (%d)", path
, errno
);
71 SharedMemoryServer::SharedMemoryServer (const char* segmentName
, SegmentOffsetType segmentSize
, uid_t uid
, gid_t gid
) :
72 mSegmentName (segmentName
), mSegmentSize (segmentSize
), mUID(SharedMemoryCommon::fixUID(uid
))
74 const mode_t perm1777
= S_ISVTX
| S_IRWXU
| S_IRWXG
| S_IRWXO
;
75 const mode_t perm0755
= S_IRWXU
| (S_IRGRP
| S_IXGRP
) | (S_IROTH
| S_IXOTH
);
76 const mode_t perm0600
= (S_IRUSR
| S_IWUSR
);
78 // make the mds directory, just in case it doesn't exist
80 makedir(SharedMemoryCommon::kMDSDirectory
, perm1777
);
81 makedir(SharedMemoryCommon::kMDSMessagesDirectory
, perm0755
);
83 // Assume kMDSMessagesDirectory was created first by securityd
84 std::string uidstr
= std::to_string(mUID
);
85 std::string upath
= SharedMemoryCommon::kMDSMessagesDirectory
;
86 upath
+= "/" + uidstr
;
87 makedir(upath
.c_str(), perm0755
);
89 mFileName
= SharedMemoryCommon::SharedMemoryFilePath(segmentName
, uid
);
92 // clean any old file away
93 unlinkfile(mFileName
.c_str());
96 secdebug("MDSPRIVACY","creating %s",mFileName
.c_str ());
98 mBackingFile
= open (mFileName
.c_str (), O_RDWR
| O_CREAT
| O_EXCL
, perm0600
);
101 mBackingFile
= open (mFileName
.c_str (), O_RDWR
| O_CREAT
| O_EXCL
, S_IRUSR
| S_IWUSR
| S_IRGRP
| S_IROTH
);
104 if (mBackingFile
< 0)
106 secdebug("MDSPRIVACY","creation of %s failed", mFileName
.c_str());
110 int rx
= fchown(mBackingFile
, uid
, gid
);
112 secdebug("MDSPRIVACY","chown of %s to %d/%d failed : %d", mFileName
.c_str(), uid
, gid
, rx
);
115 // set the segment size
116 ftruncate (mBackingFile
, segmentSize
);
118 // map it into memory
119 mSegment
= (u_int8_t
*) mmap (NULL
, mSegmentSize
, PROT_READ
| PROT_WRITE
, MAP_SHARED
, mBackingFile
, 0);
121 if (mSegment
== MAP_FAILED
) // can't map the memory?
124 unlinkfile(mFileName
.c_str());
126 mDataPtr
= mDataArea
= mSegment
+ sizeof(SegmentOffsetType
);
127 mDataMax
= mSegment
+ segmentSize
;;
129 SetProducerOffset (0);
133 SharedMemoryServer::~SharedMemoryServer ()
136 if (mSegment
== NULL
)
142 munmap (mSegment
, mSegmentSize
);
146 // mark the segment for deletion
147 unlinkfile(mFileName
.c_str ());
152 const SegmentOffsetType
154 kCRCOffset
= kSegmentLength
+ sizeof(SegmentOffsetType
),
155 kDomainOffset
= kCRCOffset
+ sizeof(SegmentOffsetType
),
156 kEventTypeOffset
= kDomainOffset
+ sizeof(SegmentOffsetType
),
157 kHeaderLength
= kEventTypeOffset
+ sizeof(SegmentOffsetType
) - kCRCOffset
;
159 void SharedMemoryServer::WriteMessage (SegmentOffsetType domain
, SegmentOffsetType event
, const void *message
, SegmentOffsetType messageLength
)
161 // backing file MUST be right size, don't ftruncate() more then needed though to avoid reaching too deep into filesystem
163 if (::fstat(mBackingFile
, &sb
) == 0 && sb
.st_size
!= (off_t
)mSegmentSize
) {
164 ::ftruncate(mBackingFile
, mSegmentSize
);
167 // assemble the final message
168 ssize_t messageSize
= kHeaderLength
+ messageLength
;
169 u_int8_t finalMessage
[messageSize
];
170 SegmentOffsetType
*fm
= (SegmentOffsetType
*) finalMessage
;
171 fm
[0] = OSSwapHostToBigInt32(domain
);
172 fm
[1] = OSSwapHostToBigInt32(event
);
173 memcpy(&fm
[2], message
, messageLength
);
175 SegmentOffsetType crc
= CalculateCRC(finalMessage
, messageSize
);
178 WriteOffset(int_cast
<size_t, SegmentOffsetType
>(messageSize
));
184 WriteData (finalMessage
, int_cast
<size_t, SegmentOffsetType
>(messageSize
));
186 // write the data count
187 SetProducerOffset(int_cast
<size_t, SegmentOffsetType
>(mDataPtr
- mDataArea
));
192 const char* SharedMemoryServer::GetSegmentName ()
194 return mSegmentName
.c_str ();
199 size_t SharedMemoryServer::GetSegmentSize ()
205 void SharedMemoryServer::SetProducerOffset (SegmentOffsetType producerCount
)
207 *((SegmentOffsetType
*) mSegment
) = OSSwapHostToBigInt32 (producerCount
);
212 void SharedMemoryServer::WriteOffset(SegmentOffsetType offset
)
215 *((u_int32_t
*) buffer
) = OSSwapHostToBigInt32(offset
);
216 WriteData(buffer
, 4);
221 void SharedMemoryServer::WriteData(const void* data
, SegmentOffsetType length
)
223 // figure out where in the buffer we actually need to write the data
224 // figure out how many bytes we can write without overflowing the buffer
225 const u_int8_t
* dp
= (const u_int8_t
*) data
;
226 SegmentOffsetType bytesToEnd
= int_cast
<ptrdiff_t, SegmentOffsetType
>(mDataMax
- mDataPtr
);
228 // figure out how many bytes we can write
229 SegmentOffsetType bytesToWrite
= (length
<= bytesToEnd
) ? length
: bytesToEnd
;
231 // move the first part of the data, making sure to skip the producer pointer
232 memcpy (mDataPtr
, dp
, bytesToWrite
);
233 mDataPtr
+= bytesToWrite
;
236 // deduct the bytes just written
237 length
-= bytesToWrite
;
239 if (length
!= 0) // did we wrap around?
241 mDataPtr
= mDataArea
;
242 memcpy (mDataPtr
, dp
, length
);