├── SUIDGuardNG
├── SUIDGuardNG.h
├── Info.plist
└── SUIDGuardNG.cpp
├── README.md
└── SUIDGuardNG.xcodeproj
└── project.pbxproj
/SUIDGuardNG/SUIDGuardNG.h:
--------------------------------------------------------------------------------
1 | /* add your code here */
2 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | SUIDGuard - A kernel extension adding mitigations to OS X to make exploitation harder
2 |
3 | Copyright (c) Stefan Esser / SektionEins GmbH, 2015. All rights reserved.
4 | stefan.esser@sektioneins.de - https://www.sektioneins.de/
5 |
6 | **OFFICIAL WEBSITE**
7 | If you came here from a media article please checkout the official website https://www.suidguard.com
8 |
9 | ## About
10 | SUIDGuard is a TrustedBSD kernel driver that implements several mitigations to protect
11 | against weaknesses in the operating system usually abused in exploits.
12 |
13 | - protects SUID/SGID root binaries from DYLD_ environment variables
14 | by overwriting the string DYLD_ with XYLD_
15 | - protects the O_APPEND flag usually used when opening e.g. logfiles
16 | from being disabled by someone with credentials that are different
17 | from those used to open the file
18 | - disallows execution of executables without a __PAGEZERO segment
19 | (stops NULL page exploits like e.g. tpwn)
20 |
21 | Tested with OS X Yosemite 10.10.5.
22 |
23 | ## Downloads for OSX 10.10.x
24 |
25 | **ATTENTION**:
26 | For ease of installation an autoloading version of this extension including
27 | a signed installer is available at
28 |
29 | DMG: https://www.suidguard.com/downloads/SUIDGuardNG-106.dmg
30 |
31 | PKG: https://www.suidguard.com/downloads/SUIDGuardNG-106.pkg
32 |
33 | (source code on GitHub might not always be latest)
34 |
35 | **WARNING**: These downloads are meant for OSX 10.10 only! Please **uninstall before upgrading to 10.10.4**!! (see issue #12 on details how to manually remove the extension after crash-on-boot)
36 |
37 | ## Downloads for OSX 10.11
38 |
39 | TBD
40 |
--------------------------------------------------------------------------------
/SUIDGuardNG/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | CFBundleDevelopmentRegion
6 | en
7 | CFBundleExecutable
8 | $(EXECUTABLE_NAME)
9 | CFBundleIdentifier
10 | $(PRODUCT_BUNDLE_IDENTIFIER)
11 | CFBundleName
12 | $(PRODUCT_NAME)
13 | CFBundlePackageType
14 | KEXT
15 | CFBundleShortVersionString
16 | 1.0.6
17 | CFBundleSignature
18 | ????
19 | CFBundleVersion
20 | 1.0.6
21 | IOKitPersonalities
22 |
23 | SUIDGUARD
24 |
25 | CFBundleIdentifier
26 | com.sektioneins.driver.$(PRODUCT_NAME:rfc1034identifier)
27 | IOClass
28 | com_sektioneins_driver_SUIDGuard
29 | IOKitDebug
30 | 0
31 | IOMatchCategory
32 | com_sektioneins_driver_SUIDGuard
33 | IOProviderClass
34 | IOResources
35 | IOResourceMatch
36 | IOBSD
37 |
38 |
39 | NSHumanReadableCopyright
40 | Copyright © 2016 SektionEins GmbH. All rights reserved.
41 | OSBundleLibraries
42 |
43 | com.apple.kpi.bsd
44 | 14.0
45 | com.apple.kpi.dsep
46 | 14.0
47 | com.apple.kpi.iokit
48 | 14.0
49 | com.apple.kpi.libkern
50 | 14.0
51 | com.apple.kpi.mach
52 | 14.0
53 | com.apple.kpi.unsupported
54 | 14.0
55 |
56 | OSBundleRequired
57 | Root
58 |
59 |
60 |
--------------------------------------------------------------------------------
/SUIDGuardNG.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 46;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | B1EF6CB61B61C3E9008C5BD4 /* SUIDGuardNG.h in Headers */ = {isa = PBXBuildFile; fileRef = B1EF6CB51B61C3E9008C5BD4 /* SUIDGuardNG.h */; };
11 | B1EF6CB81B61C3E9008C5BD4 /* SUIDGuardNG.cpp in Sources */ = {isa = PBXBuildFile; fileRef = B1EF6CB71B61C3E9008C5BD4 /* SUIDGuardNG.cpp */; };
12 | /* End PBXBuildFile section */
13 |
14 | /* Begin PBXFileReference section */
15 | B1EF6CB01B61C3E9008C5BD4 /* SUIDGuardNG.kext */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = SUIDGuardNG.kext; sourceTree = BUILT_PRODUCTS_DIR; };
16 | B1EF6CB41B61C3E9008C5BD4 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
17 | B1EF6CB51B61C3E9008C5BD4 /* SUIDGuardNG.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SUIDGuardNG.h; sourceTree = ""; };
18 | B1EF6CB71B61C3E9008C5BD4 /* SUIDGuardNG.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = SUIDGuardNG.cpp; sourceTree = ""; };
19 | /* End PBXFileReference section */
20 |
21 | /* Begin PBXFrameworksBuildPhase section */
22 | B1EF6CAC1B61C3E9008C5BD4 /* Frameworks */ = {
23 | isa = PBXFrameworksBuildPhase;
24 | buildActionMask = 2147483647;
25 | files = (
26 | );
27 | runOnlyForDeploymentPostprocessing = 0;
28 | };
29 | /* End PBXFrameworksBuildPhase section */
30 |
31 | /* Begin PBXGroup section */
32 | B1EF6CA61B61C3E9008C5BD4 = {
33 | isa = PBXGroup;
34 | children = (
35 | B1EF6CB21B61C3E9008C5BD4 /* SUIDGuardNG */,
36 | B1EF6CB11B61C3E9008C5BD4 /* Products */,
37 | );
38 | sourceTree = "";
39 | };
40 | B1EF6CB11B61C3E9008C5BD4 /* Products */ = {
41 | isa = PBXGroup;
42 | children = (
43 | B1EF6CB01B61C3E9008C5BD4 /* SUIDGuardNG.kext */,
44 | );
45 | name = Products;
46 | sourceTree = "";
47 | };
48 | B1EF6CB21B61C3E9008C5BD4 /* SUIDGuardNG */ = {
49 | isa = PBXGroup;
50 | children = (
51 | B1EF6CB51B61C3E9008C5BD4 /* SUIDGuardNG.h */,
52 | B1EF6CB71B61C3E9008C5BD4 /* SUIDGuardNG.cpp */,
53 | B1EF6CB31B61C3E9008C5BD4 /* Supporting Files */,
54 | );
55 | path = SUIDGuardNG;
56 | sourceTree = "";
57 | };
58 | B1EF6CB31B61C3E9008C5BD4 /* Supporting Files */ = {
59 | isa = PBXGroup;
60 | children = (
61 | B1EF6CB41B61C3E9008C5BD4 /* Info.plist */,
62 | );
63 | name = "Supporting Files";
64 | sourceTree = "";
65 | };
66 | /* End PBXGroup section */
67 |
68 | /* Begin PBXHeadersBuildPhase section */
69 | B1EF6CAD1B61C3E9008C5BD4 /* Headers */ = {
70 | isa = PBXHeadersBuildPhase;
71 | buildActionMask = 2147483647;
72 | files = (
73 | B1EF6CB61B61C3E9008C5BD4 /* SUIDGuardNG.h in Headers */,
74 | );
75 | runOnlyForDeploymentPostprocessing = 0;
76 | };
77 | /* End PBXHeadersBuildPhase section */
78 |
79 | /* Begin PBXNativeTarget section */
80 | B1EF6CAF1B61C3E9008C5BD4 /* SUIDGuardNG */ = {
81 | isa = PBXNativeTarget;
82 | buildConfigurationList = B1EF6CBB1B61C3E9008C5BD4 /* Build configuration list for PBXNativeTarget "SUIDGuardNG" */;
83 | buildPhases = (
84 | B1EF6CAB1B61C3E9008C5BD4 /* Sources */,
85 | B1EF6CAC1B61C3E9008C5BD4 /* Frameworks */,
86 | B1EF6CAD1B61C3E9008C5BD4 /* Headers */,
87 | B1EF6CAE1B61C3E9008C5BD4 /* Resources */,
88 | );
89 | buildRules = (
90 | );
91 | dependencies = (
92 | );
93 | name = SUIDGuardNG;
94 | productName = SUIDGuardNG;
95 | productReference = B1EF6CB01B61C3E9008C5BD4 /* SUIDGuardNG.kext */;
96 | productType = "com.apple.product-type.kernel-extension";
97 | };
98 | /* End PBXNativeTarget section */
99 |
100 | /* Begin PBXProject section */
101 | B1EF6CA71B61C3E9008C5BD4 /* Project object */ = {
102 | isa = PBXProject;
103 | attributes = {
104 | LastUpgradeCheck = 0720;
105 | ORGANIZATIONNAME = "SektionEins GmbH";
106 | TargetAttributes = {
107 | B1EF6CAF1B61C3E9008C5BD4 = {
108 | CreatedOnToolsVersion = 6.4;
109 | };
110 | };
111 | };
112 | buildConfigurationList = B1EF6CAA1B61C3E9008C5BD4 /* Build configuration list for PBXProject "SUIDGuardNG" */;
113 | compatibilityVersion = "Xcode 3.2";
114 | developmentRegion = English;
115 | hasScannedForEncodings = 0;
116 | knownRegions = (
117 | en,
118 | );
119 | mainGroup = B1EF6CA61B61C3E9008C5BD4;
120 | productRefGroup = B1EF6CB11B61C3E9008C5BD4 /* Products */;
121 | projectDirPath = "";
122 | projectRoot = "";
123 | targets = (
124 | B1EF6CAF1B61C3E9008C5BD4 /* SUIDGuardNG */,
125 | );
126 | };
127 | /* End PBXProject section */
128 |
129 | /* Begin PBXResourcesBuildPhase section */
130 | B1EF6CAE1B61C3E9008C5BD4 /* Resources */ = {
131 | isa = PBXResourcesBuildPhase;
132 | buildActionMask = 2147483647;
133 | files = (
134 | );
135 | runOnlyForDeploymentPostprocessing = 0;
136 | };
137 | /* End PBXResourcesBuildPhase section */
138 |
139 | /* Begin PBXSourcesBuildPhase section */
140 | B1EF6CAB1B61C3E9008C5BD4 /* Sources */ = {
141 | isa = PBXSourcesBuildPhase;
142 | buildActionMask = 2147483647;
143 | files = (
144 | B1EF6CB81B61C3E9008C5BD4 /* SUIDGuardNG.cpp in Sources */,
145 | );
146 | runOnlyForDeploymentPostprocessing = 0;
147 | };
148 | /* End PBXSourcesBuildPhase section */
149 |
150 | /* Begin XCBuildConfiguration section */
151 | B1EF6CB91B61C3E9008C5BD4 /* Debug */ = {
152 | isa = XCBuildConfiguration;
153 | buildSettings = {
154 | ALWAYS_SEARCH_USER_PATHS = NO;
155 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
156 | CLANG_CXX_LIBRARY = "libc++";
157 | CLANG_ENABLE_MODULES = YES;
158 | CLANG_ENABLE_OBJC_ARC = YES;
159 | CLANG_WARN_BOOL_CONVERSION = YES;
160 | CLANG_WARN_CONSTANT_CONVERSION = YES;
161 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
162 | CLANG_WARN_EMPTY_BODY = YES;
163 | CLANG_WARN_ENUM_CONVERSION = YES;
164 | CLANG_WARN_INT_CONVERSION = YES;
165 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
166 | CLANG_WARN_UNREACHABLE_CODE = YES;
167 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
168 | COPY_PHASE_STRIP = NO;
169 | DEBUG_INFORMATION_FORMAT = dwarf;
170 | ENABLE_STRICT_OBJC_MSGSEND = YES;
171 | ENABLE_TESTABILITY = YES;
172 | GCC_C_LANGUAGE_STANDARD = gnu99;
173 | GCC_DYNAMIC_NO_PIC = NO;
174 | GCC_NO_COMMON_BLOCKS = YES;
175 | GCC_OPTIMIZATION_LEVEL = 0;
176 | GCC_PREPROCESSOR_DEFINITIONS = (
177 | "DEBUG=1",
178 | "$(inherited)",
179 | );
180 | GCC_SYMBOLS_PRIVATE_EXTERN = NO;
181 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
182 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
183 | GCC_WARN_UNDECLARED_SELECTOR = YES;
184 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
185 | GCC_WARN_UNUSED_FUNCTION = YES;
186 | GCC_WARN_UNUSED_VARIABLE = YES;
187 | MACOSX_DEPLOYMENT_TARGET = 10.11;
188 | MTL_ENABLE_DEBUG_INFO = YES;
189 | ONLY_ACTIVE_ARCH = YES;
190 | SDKROOT = macosx;
191 | };
192 | name = Debug;
193 | };
194 | B1EF6CBA1B61C3E9008C5BD4 /* Release */ = {
195 | isa = XCBuildConfiguration;
196 | buildSettings = {
197 | ALWAYS_SEARCH_USER_PATHS = NO;
198 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
199 | CLANG_CXX_LIBRARY = "libc++";
200 | CLANG_ENABLE_MODULES = YES;
201 | CLANG_ENABLE_OBJC_ARC = YES;
202 | CLANG_WARN_BOOL_CONVERSION = YES;
203 | CLANG_WARN_CONSTANT_CONVERSION = YES;
204 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
205 | CLANG_WARN_EMPTY_BODY = YES;
206 | CLANG_WARN_ENUM_CONVERSION = YES;
207 | CLANG_WARN_INT_CONVERSION = YES;
208 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
209 | CLANG_WARN_UNREACHABLE_CODE = YES;
210 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
211 | COPY_PHASE_STRIP = NO;
212 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
213 | ENABLE_NS_ASSERTIONS = NO;
214 | ENABLE_STRICT_OBJC_MSGSEND = YES;
215 | GCC_C_LANGUAGE_STANDARD = gnu99;
216 | GCC_NO_COMMON_BLOCKS = YES;
217 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
218 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
219 | GCC_WARN_UNDECLARED_SELECTOR = YES;
220 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
221 | GCC_WARN_UNUSED_FUNCTION = YES;
222 | GCC_WARN_UNUSED_VARIABLE = YES;
223 | MACOSX_DEPLOYMENT_TARGET = 10.11;
224 | MTL_ENABLE_DEBUG_INFO = NO;
225 | SDKROOT = macosx;
226 | };
227 | name = Release;
228 | };
229 | B1EF6CBC1B61C3E9008C5BD4 /* Debug */ = {
230 | isa = XCBuildConfiguration;
231 | buildSettings = {
232 | CODE_SIGN_IDENTITY = "Developer ID Application";
233 | COMBINE_HIDPI_IMAGES = YES;
234 | CURRENT_PROJECT_VERSION = 1.0.6d;
235 | INFOPLIST_FILE = SUIDGuardNG/Info.plist;
236 | MODULE_NAME = com.sektioneins.kext.SUIDGuardNG;
237 | MODULE_START = SUIDGuard_start;
238 | MODULE_STOP = SUIDGuard_stop;
239 | MODULE_VERSION = 1.0.6;
240 | PRODUCT_BUNDLE_IDENTIFIER = "com.sektioneins.driver.$(PRODUCT_NAME:rfc1034identifier)";
241 | PRODUCT_NAME = "$(TARGET_NAME)";
242 | WRAPPER_EXTENSION = kext;
243 | };
244 | name = Debug;
245 | };
246 | B1EF6CBD1B61C3E9008C5BD4 /* Release */ = {
247 | isa = XCBuildConfiguration;
248 | buildSettings = {
249 | CODE_SIGN_IDENTITY = "Developer ID Application";
250 | COMBINE_HIDPI_IMAGES = YES;
251 | CURRENT_PROJECT_VERSION = 1.0.6;
252 | INFOPLIST_FILE = SUIDGuardNG/Info.plist;
253 | MODULE_NAME = com.sektioneins.kext.SUIDGuardNG;
254 | MODULE_START = SUIDGuard_start;
255 | MODULE_STOP = SUIDGuard_stop;
256 | MODULE_VERSION = 1.0.6;
257 | PRODUCT_BUNDLE_IDENTIFIER = "com.sektioneins.driver.$(PRODUCT_NAME:rfc1034identifier)";
258 | PRODUCT_NAME = "$(TARGET_NAME)";
259 | WRAPPER_EXTENSION = kext;
260 | };
261 | name = Release;
262 | };
263 | /* End XCBuildConfiguration section */
264 |
265 | /* Begin XCConfigurationList section */
266 | B1EF6CAA1B61C3E9008C5BD4 /* Build configuration list for PBXProject "SUIDGuardNG" */ = {
267 | isa = XCConfigurationList;
268 | buildConfigurations = (
269 | B1EF6CB91B61C3E9008C5BD4 /* Debug */,
270 | B1EF6CBA1B61C3E9008C5BD4 /* Release */,
271 | );
272 | defaultConfigurationIsVisible = 0;
273 | defaultConfigurationName = Release;
274 | };
275 | B1EF6CBB1B61C3E9008C5BD4 /* Build configuration list for PBXNativeTarget "SUIDGuardNG" */ = {
276 | isa = XCConfigurationList;
277 | buildConfigurations = (
278 | B1EF6CBC1B61C3E9008C5BD4 /* Debug */,
279 | B1EF6CBD1B61C3E9008C5BD4 /* Release */,
280 | );
281 | defaultConfigurationIsVisible = 0;
282 | defaultConfigurationName = Release;
283 | };
284 | /* End XCConfigurationList section */
285 | };
286 | rootObject = B1EF6CA71B61C3E9008C5BD4 /* Project object */;
287 | }
288 |
--------------------------------------------------------------------------------
/SUIDGuardNG/SUIDGuardNG.cpp:
--------------------------------------------------------------------------------
1 | //
2 | // SUIDGuarNGd.cpp
3 | // SUIDGuard
4 | //
5 | // Created by Stefan Esser on 15/07/15.
6 | // Copyright (c) 2015 SektionEins GmbH. All rights reserved.
7 | //
8 |
9 | #include
10 | #include
11 |
12 | extern "C" {
13 |
14 |
15 | #include
16 | #include
17 | #include
18 | #include
19 | #include
20 | #include
21 | #include
22 | #include
23 | #include
24 | #define CONFIG_MACF 1
25 | #include
26 | #include
27 | #include
28 |
29 | /* we have to copy these structs because we need access to fg_cred */
30 |
31 | /* file types */
32 | typedef enum {
33 | DTYPE_VNODE = 1, /* file */
34 | DTYPE_SOCKET, /* communications endpoint */
35 | DTYPE_PSXSHM, /* POSIX Shared memory */
36 | DTYPE_PSXSEM, /* POSIX Semaphores */
37 | DTYPE_KQUEUE, /* kqueue */
38 | DTYPE_PIPE, /* pipe */
39 | DTYPE_FSEVENTS, /* fsevents */
40 | DTYPE_ATALK /* (obsolete) */
41 | } file_type_t;
42 |
43 | struct fileglob {
44 | LIST_ENTRY(fileglob) f_msglist;/* list of active files */
45 | int32_t fg_flag; /* see fcntl.h */
46 | int32_t fg_count; /* reference count */
47 | int32_t fg_msgcount; /* references from message queue */
48 | int32_t fg_lflags; /* file global flags */
49 | kauth_cred_t fg_cred; /* credentials associated with descriptor */
50 | const struct fileops {
51 | file_type_t fo_type; /* descriptor type */
52 | int (*fo_read) (struct fileproc *fp, struct uio *uio,
53 | int flags, vfs_context_t ctx);
54 | int (*fo_write) (struct fileproc *fp, struct uio *uio,
55 | int flags, vfs_context_t ctx);
56 | #define FOF_OFFSET 0x00000001 /* offset supplied to vn_write */
57 | #define FOF_PCRED 0x00000002 /* cred from proc, not current thread */
58 | int (*fo_ioctl) (struct fileproc *fp, u_long com,
59 | caddr_t data, vfs_context_t ctx);
60 | int (*fo_select) (struct fileproc *fp, int which,
61 | void *wql, vfs_context_t ctx);
62 | int (*fo_close) (struct fileglob *fg, vfs_context_t ctx);
63 | int (*fo_kqfilter) (struct fileproc *fp, struct knote *kn,
64 | vfs_context_t ctx);
65 | int (*fo_drain) (struct fileproc *fp, vfs_context_t ctx);
66 | } *fg_ops;
67 | off_t fg_offset;
68 | void *fg_data; /* vnode or socket or SHM or semaphore */
69 | void *fg_vn_data; /* Per fd vnode data, used for directories */
70 | };
71 |
72 |
73 | struct __vm_map
74 | {
75 | void * lck1, * lck2;
76 | void * links_prev, * links_next;
77 | uint64_t min_offset;
78 | };
79 |
80 | /* For an unknown reason Apple considers proc_task to be a private API.
81 | Annoying when you want to create a security product. */
82 | void * proc_task(void * proc) {
83 | uint64_t * p = (uint64_t *)proc;
84 | return (void *)p[3];
85 | }
86 |
87 | /* Not a private API but missing from the headers ... */
88 | void * get_task_map(void* t);
89 |
90 | /* purpose of this hook is to detect execution of SUID/SGID root binaries and
91 | when found it will scan the environment variables for this process in
92 | kernel memory and overwrite all DYLD_ variables to protect against weaknesses
93 | in the dyld code */
94 | int suidguard_cred_label_update_execve(kauth_cred_t old_cred, kauth_cred_t new_cred, struct proc *p, struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *vnodelabel, struct label *scriptvnodelabel, struct label *execlabel, u_int *csflags, void *macpolicyattr, size_t macpolicyattrlen, int *disjointp)
95 | {
96 | struct image_params *imgp;
97 |
98 | /* get the current map and check its min_offet */
99 | struct __vm_map * map = (struct __vm_map *) get_task_map(proc_task(p));
100 | if (map->min_offset < 0x1000) {
101 | printf("SUIDGuard: disallowed execution of binary without a __PAGEZERO segment\n");
102 | return 1; /* disallow execution */
103 | }
104 |
105 | /* we can determine address of image_params structure from the csflags pointer */
106 | /* some might consider this a dirty hack, but Apple makes it necessary */
107 | imgp = (struct image_params *)((unsigned char *)csflags-offsetof(struct image_params, ip_csflags));
108 |
109 | struct vnode_attr va;
110 | int error = 0;
111 | vfs_context_t ctx = NULL;
112 |
113 | /* ignore all non regular files */
114 | if (!vnode_isreg(vp)) {
115 | goto exit;
116 | }
117 |
118 | /* create a new context */
119 | if ((ctx = vfs_context_create(NULL)) == NULL) {
120 | error = ENOMEM;
121 | goto exit;
122 | }
123 |
124 | /* we only need a subset of the info */
125 | VATTR_INIT(&va);
126 | VATTR_WANTED(&va, va_uid);
127 | VATTR_WANTED(&va, va_gid);
128 | VATTR_WANTED(&va, va_mode);
129 | if ((error = vnode_getattr(vp, &va, ctx))) {
130 | goto exit;
131 | }
132 |
133 | /* now check if this is a SUID/SGID root binary */
134 | if ((va.va_mode & (VSUID|VSGID)) && ((va.va_uid == 0) || (va.va_gid == 0))) {
135 |
136 | int i;
137 | int found = 0;
138 |
139 | /* scan all the environment variables and disallow */
140 | /* all DYLD_ variables to protect from flaws in dyld */
141 |
142 | char *tmp = imgp->ip_endargv;
143 | for (i=0; iip_envc; i++) {
144 | if (strncmp(tmp, "DYLD_", 5) == 0) {
145 | tmp[0] = 'X';
146 | found++;
147 | if (found <= 5) {
148 | printf("SUIDGuard: found and neutralized DYLD_ environment variable D%s for SUID/SGID root binary\n", tmp+1);
149 | }
150 | }
151 | tmp += strlen(tmp)+1;
152 | }
153 | if (found > 5) {
154 | printf("SUIDGuard: found and neutralized more than 5 DYLD_ environment variables for SUID/SGID root binary - skipping others to not flood log file\n");
155 | }
156 | }
157 |
158 | exit:
159 | if (ctx) {
160 | vfs_context_rele(ctx);
161 | }
162 |
163 | /* maybe better to disallow execution whenever an error happened before */
164 | if (error != 0) {
165 | return 1;
166 | }
167 |
168 | return 0;
169 | }
170 |
171 |
172 | /* we hook this policy hook and return 1 to activate the transition code */
173 | int suidguard_cred_check_label_update_execve(kauth_cred_t old, struct vnode *vp, off_t offset, struct vnode *scriptvp, struct label *vnodelabel, struct label *scriptvnodelabel, struct label *execlabel, struct proc *p, void *macpolicyattr, size_t macpolicyattrlen)
174 | {
175 | /* AppleMobileFileIntegrity does this already but better not rely on it */
176 | return 1;
177 | }
178 |
179 | /* we hook into fcntl() because it is generally a bad idea to allow deactivation
180 | of O_APPEND for files opened with the credentials of another user */
181 | int suidguard_file_check_fcntl(kauth_cred_t cred, struct fileglob *fg, struct label *label, int cmd, user_long_t arg)
182 | {
183 | /* we only react if someone tries to use F_SETFL */
184 | if (cmd != F_SETFL) {
185 | return 0;
186 | }
187 |
188 | /* ignore if this file is not opened with append */
189 | if ((fg->fg_flag & FAPPEND) == 0) {
190 | return 0;
191 | }
192 |
193 | /* ignore if we are the super-user */
194 | if (kauth_cred_issuser(cred)) {
195 | return 0;
196 | }
197 |
198 | /* ignore if we own the file */
199 | if (kauth_cred_getuid(cred) == kauth_cred_getuid(fg->fg_cred)) {
200 | return 0;
201 | }
202 |
203 | /* ignore if caller is not trying to clear FAPPEND */
204 | if (arg & FAPPEND) {
205 | return 0;
206 | }
207 |
208 | /* for now log this attempt and deny */
209 | printf("SUIDGuard: blocked attempt from uid %u to clear O_APPEND flag on file owned by %u\n", kauth_cred_getuid(cred), kauth_cred_getuid(fg->fg_cred));
210 | return EPERM;
211 | }
212 |
213 | static mac_policy_handle_t suidguard_handle = 0;
214 |
215 | static struct mac_policy_ops suidguard_ops =
216 | {
217 | .mpo_cred_check_label_update_execve = suidguard_cred_check_label_update_execve,
218 | .mpo_cred_label_update_execve = suidguard_cred_label_update_execve,
219 | .mpo_file_check_fcntl = suidguard_file_check_fcntl
220 | };
221 |
222 | static struct mac_policy_conf suidguard_policy_conf = {
223 | .mpc_name = "suidguard",
224 | .mpc_fullname = "SUID Guard Kernel Extension",
225 | .mpc_labelnames = NULL,
226 | .mpc_labelname_count = 0,
227 | .mpc_ops = &suidguard_ops,
228 | .mpc_loadtime_flags = MPC_LOADTIME_FLAG_UNLOADOK,
229 | .mpc_field_off = NULL,
230 | .mpc_runtime_flags = 0
231 | };
232 |
233 | kern_return_t SUIDGuard_start(kmod_info_t * ki, void *d);
234 | kern_return_t SUIDGuard_stop(kmod_info_t *ki, void *d);
235 |
236 | kern_return_t SUIDGuard_start(kmod_info_t * ki, void *d)
237 | {
238 | int r = mac_policy_register(&suidguard_policy_conf, &suidguard_handle, d);
239 | return r;
240 | }
241 |
242 | kern_return_t SUIDGuard_stop(kmod_info_t *ki, void *d)
243 | {
244 | int r = mac_policy_unregister(suidguard_handle);
245 | return r;
246 | }
247 | }
248 |
249 | class com_sektioneins_driver_SUIDGuard : public IOService
250 | {
251 | OSDeclareDefaultStructors(com_sektioneins_driver_SUIDGuard)
252 | public:
253 | virtual bool init(OSDictionary *dictionary = 0);
254 | virtual void free(void);
255 | virtual IOService *probe(IOService *provider, SInt32 *score);
256 | virtual bool start(IOService *provider);
257 | virtual void stop(IOService *provider);
258 | };
259 |
260 | // This required macro defines the class's constructors, destructors,
261 | // and several other methods I/O Kit requires.
262 | OSDefineMetaClassAndStructors(com_sektioneins_driver_SUIDGuard, IOService)
263 |
264 | // Define the driver's superclass.
265 | #define super IOService
266 |
267 | bool com_sektioneins_driver_SUIDGuard::init(OSDictionary *dict)
268 | {
269 | bool result = super::init(dict);
270 | //SUIDGuard_start(NULL, NULL);
271 | return result;
272 | }
273 |
274 | void com_sektioneins_driver_SUIDGuard::free(void)
275 | {
276 | //SUIDGuard_stop(NULL, NULL);
277 | super::free();
278 | }
279 |
280 | IOService *com_sektioneins_driver_SUIDGuard::probe(IOService *provider,
281 | SInt32 *score)
282 | {
283 | IOService *result = super::probe(provider, score);
284 | return result;
285 | }
286 |
287 | bool com_sektioneins_driver_SUIDGuard::start(IOService *provider)
288 | {
289 | bool result = super::start(provider);
290 | return result;
291 | }
292 |
293 | void com_sektioneins_driver_SUIDGuard::stop(IOService *provider)
294 | {
295 | super::stop(provider);
296 | }
297 |
--------------------------------------------------------------------------------