2 #include "open_directory.h"
7 #include <sys/sysctl.h>
8 #include <OpenDirectory/OpenDirectoryPriv.h>
9 #include <DirectoryService/DirServicesTypes.h>
11 /*---------------------------------------------------------------------------
12 * PUBLIC setrestricted - sets the restricted flag
13 *---------------------------------------------------------------------------*/
15 setrestricted(CFDictionaryRef attrs
)
17 const char* user_allowed
[] = { "shell", "full name", "office location", "office phone", "home phone", "picture", NULL
};
18 const char* root_restricted
[] = { "password", "change", "expire", "class", NULL
};
21 int restrict_by_default
= !master_mode
;
23 // for ordinary users, everything is restricted except for the values
24 // expressly permitted above
25 // for root, everything is permitted except for the values expressly
28 for (ep
= list
; ep
->prompt
; ep
++) {
29 ep
->restricted
= restrict_by_default
;
30 pp
= restrict_by_default
? user_allowed
: root_restricted
;
32 if (strncasecmp(ep
->prompt
, *pp
, ep
->len
) == 0) {
33 ep
->restricted
= !restrict_by_default
;
38 // If not root, then it is only permitted to change the shell
39 // when the original value is one of the approved shells.
40 // Otherwise, the assumption is that root has given this user
41 // a restricted shell which they must not change away from.
42 if (restrict_by_default
&& strcmp(ep
->prompt
, "shell") == 0) {
44 CFArrayRef values
= CFDictionaryGetValue(attrs
, CFSTR(kDS1AttrUserShell
));
45 CFTypeRef value
= values
&& CFArrayGetCount(values
) > 0 ? CFArrayGetValueAtIndex(values
, 0) : NULL
;
46 if (value
&& CFGetTypeID(value
) == CFStringGetTypeID()) {
47 size_t size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(value
), kCFStringEncodingUTF8
)+1;
48 char* shell
= malloc(size
);
49 if (CFStringGetCString(value
, shell
, size
, kCFStringEncodingUTF8
)) {
50 if (ok_shell(shell
)) {
60 prompt_passwd(CFStringRef user
)
62 CFStringRef result
= NULL
;
63 CFStringRef prompt
= CFStringCreateWithFormat(NULL
, NULL
, CFSTR("Password for %@: "), user
);
65 CFStringGetCString(prompt
, buf
, sizeof(buf
), kCFStringEncodingUTF8
);
66 char* pass
= getpass(buf
);
67 result
= CFStringCreateWithCString(NULL
, pass
, kCFStringEncodingUTF8
);
68 memset(pass
, 0, strlen(pass
));
74 show_error(CFErrorRef error
) {
76 CFStringRef desc
= CFErrorCopyDescription(error
);
78 cfprintf(stderr
, "%s: %@", progname
, desc
);
81 desc
= CFErrorCopyFailureReason(error
);
82 if (desc
) cfprintf(stderr
, " %@", desc
);
84 desc
= CFErrorCopyRecoverySuggestion(error
);
85 if (desc
) cfprintf(stderr
, " %@", desc
);
87 fprintf(stderr
, "\n");
94 size_t susz
= sizeof(su
);
95 if (sysctlbyname("kern.singleuser", &su
, &susz
, NULL
, 0) != 0) {
103 load_DirectoryServicesLocal() {
104 const char* launchctl
= "/bin/launchctl";
105 const char* plist
= "/System/Library/LaunchDaemons/com.apple.DirectoryServicesLocal.plist";
114 execl(launchctl
, launchctl
, "load", plist
, NULL
);
121 res
= waitpid(pid
, &status
, 0);
122 } while (res
== -1 && errno
== EINTR
);
129 return (WIFEXITED(status
) && (WEXITSTATUS(status
) == EXIT_SUCCESS
));
133 odGetUser(CFStringRef location
, CFStringRef authname
, CFStringRef user
, CFDictionaryRef
* attrs
)
135 ODSessionRef session
= NULL
;
136 ODNodeRef node
= NULL
;
137 ODRecordRef rec
= NULL
;
138 CFErrorRef error
= NULL
;
143 * Connect to DS server
145 session
= ODSessionCreate(NULL
, NULL
, &error
);
146 if ( !session
&& error
&& CFErrorGetCode(error
) == eServerNotRunning
) {
148 * In single-user mode, attempt to load the local DS daemon.
150 if (is_singleuser() && load_DirectoryServicesLocal()) {
151 CFTypeRef keys
[] = { kODSessionLocalPath
};
152 CFTypeRef vals
[] = { CFSTR("/var/db/dslocal") };
153 CFDictionaryRef opts
= CFDictionaryCreate(NULL
, keys
, vals
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
155 session
= ODSessionCreate(NULL
, opts
, &error
);
160 location
= CFRetain(CFSTR("/Local/Default"));
169 * Open the specified node, or perform a search.
170 * Copy the record and put the record's location into DSPath.
173 node
= ODNodeCreateWithName(NULL
, session
, location
, &error
);
175 node
= ODNodeCreateWithNodeType(NULL
, session
, kODTypeAuthenticationSearchNode
, &error
);
177 if (session
) CFRelease(session
);
179 CFTypeRef vals
[] = { CFSTR(kDSAttributesStandardAll
) };
180 CFArrayRef desiredAttrs
= CFArrayCreate(NULL
, vals
, 1, &kCFTypeArrayCallBacks
);
181 rec
= ODNodeCopyRecord(node
, CFSTR(kDSStdRecordTypeUsers
), user
, desiredAttrs
, &error
);
182 if (desiredAttrs
) CFRelease(desiredAttrs
);
186 *attrs
= ODRecordCopyDetails(rec
, NULL
, &error
);
188 CFArrayRef values
= CFDictionaryGetValue(*attrs
, CFSTR(kDSNAttrMetaNodeLocation
));
189 DSPath
= (values
&& CFArrayGetCount(values
) > 0) ? CFArrayGetValueAtIndex(values
, 0) : NULL
;
193 * Prompt for a password if -u was specified,
194 * or if we are not root,
195 * or if we are updating something not on the
198 if (authname
|| !master_mode
||
199 (DSPath
&& CFStringCompareWithOptions(DSPath
, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo
)) {
201 CFStringRef password
= NULL
;
203 if (!authname
) authname
= user
;
205 password
= prompt_passwd(authname
);
206 if (!ODRecordSetNodeCredentials(rec
, authname
, password
, &error
)) {
213 if (error
) show_error(error
);
218 odUpdateUser(ODRecordRef rec
, CFDictionaryRef attrs_orig
, CFDictionaryRef attrs
)
220 CFErrorRef error
= NULL
;
224 for (ep
= list
; ep
->prompt
; ep
++) {
227 if (!rec
|| !attrs_orig
|| !attrs
) break;
229 // No need to update if entry is restricted
230 if (ep
->restricted
) continue;
232 CFArrayRef values_orig
= CFDictionaryGetValue(attrs_orig
, ep
->attrName
);
233 CFTypeRef value_orig
= values_orig
&& CFArrayGetCount(values_orig
) ? CFArrayGetValueAtIndex(values_orig
, 0) : NULL
;
234 CFTypeRef value
= CFDictionaryGetValue(attrs
, ep
->attrName
);
236 // No need to update if both values are the same
237 if (value
== value_orig
) continue;
239 // No need to update if strings are equal
240 if (value
&& value_orig
) {
241 if (CFGetTypeID(value_orig
) == CFStringGetTypeID() &&
242 CFStringCompare(value_orig
, value
, 0) == kCFCompareEqualTo
) continue;
245 // No need to update if empty string replaces NULL
246 if (!value_orig
&& value
) {
247 if (CFStringGetLength(value
) == 0) continue;
252 // if new value is an empty string, send an empty dictionary which will delete the property.
253 CFIndex count
= CFEqual(value
, CFSTR("")) ? 0 : 1;
254 CFTypeRef vals
[] = { value
};
255 CFArrayRef values
= CFArrayCreate(NULL
, vals
, count
, &kCFTypeArrayCallBacks
);
256 if (values
&& ODRecordSetValues(rec
, ep
->attrName
, values
, &error
)) {
259 if (values
) CFRelease(values
);
260 if (error
) show_error(error
);
265 updated
= ODRecordSynchronize(rec
, &error
);
266 if (error
) show_error(error
);
269 fprintf(stderr
, "%s: no changes made\n", progname
);
272 #endif /* OPEN_DIRECTORY */