]> git.saurik.com Git - apple/system_cmds.git/blob - chpass.tproj/open_directory.c
system_cmds-735.tar.gz
[apple/system_cmds.git] / chpass.tproj / open_directory.c
1 #ifdef OPEN_DIRECTORY
2 #include "open_directory.h"
3 #include "chpass.h"
4 #include <err.h>
5 #include <sys/time.h>
6 #include <unistd.h>
7 #include <sys/sysctl.h>
8 #include <OpenDirectory/OpenDirectory.h>
9 #include <OpenDirectory/OpenDirectoryPriv.h>
10
11 /*---------------------------------------------------------------------------
12 * PUBLIC setrestricted - sets the restricted flag
13 *---------------------------------------------------------------------------*/
14 void
15 setrestricted(CFDictionaryRef attrs)
16 {
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 };
19 ENTRY* ep;
20 const char** pp;
21 int restrict_by_default = !master_mode;
22
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
26 // restricted above
27
28 for (ep = list; ep->prompt; ep++) {
29 ep->restricted = restrict_by_default;
30 pp = restrict_by_default ? user_allowed : root_restricted;
31 for (; *pp; pp++) {
32 if (strncasecmp(ep->prompt, *pp, ep->len) == 0) {
33 ep->restricted = !restrict_by_default;
34 break;
35 }
36 }
37
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) {
43 ep->restricted = 1;
44 CFArrayRef values = CFDictionaryGetValue(attrs, kODAttributeTypeUserShell);
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)) {
51 ep->restricted = 0;
52 }
53 }
54 }
55 }
56 }
57 }
58
59 static CFStringRef
60 prompt_passwd(CFStringRef user)
61 {
62 CFStringRef result = NULL;
63 CFStringRef prompt = CFStringCreateWithFormat(NULL, NULL, CFSTR("Password for %@: "), user);
64 size_t prompt_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(prompt), kCFStringEncodingUTF8);
65 char* buf = malloc(prompt_size);
66 CFStringGetCString(prompt, buf, prompt_size, kCFStringEncodingUTF8);
67 char* pass = getpass(buf);
68 result = CFStringCreateWithCString(NULL, pass, kCFStringEncodingUTF8);
69 memset(pass, 0, strlen(pass));
70 free(buf);
71 CFRelease(prompt);
72 return result;
73 }
74
75 static void
76 show_error(CFErrorRef error) {
77 if (error) {
78 CFStringRef desc = CFErrorCopyDescription(error);
79 if (desc) {
80 cfprintf(stderr, "%s: %@", progname, desc);
81 CFRelease(desc);
82 }
83 desc = CFErrorCopyFailureReason(error);
84 if (desc) cfprintf(stderr, " %@", desc);
85
86 desc = CFErrorCopyRecoverySuggestion(error);
87 if (desc) cfprintf(stderr, " %@", desc);
88
89 fprintf(stderr, "\n");
90 }
91 }
92
93 ODRecordRef
94 odGetUser(CFStringRef location, CFStringRef authname, CFStringRef user, CFDictionaryRef* attrs)
95 {
96 ODNodeRef node = NULL;
97 ODRecordRef rec = NULL;
98 CFErrorRef error = NULL;
99
100 assert(attrs);
101
102 /*
103 * Open the specified node, or perform a search.
104 * Copy the record and put the record's location into DSPath.
105 */
106 if (location) {
107 node = ODNodeCreateWithName(NULL, kODSessionDefault, location, &error);
108 } else {
109 node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODNodeTypeAuthentication, &error);
110 }
111 if (node) {
112 CFTypeRef vals[] = { kODAttributeTypeStandardOnly };
113 CFArrayRef desiredAttrs = CFArrayCreate(NULL, vals, 1, &kCFTypeArrayCallBacks);
114 rec = ODNodeCopyRecord(node, kODRecordTypeUsers, user, desiredAttrs, &error);
115 if (desiredAttrs) CFRelease(desiredAttrs);
116 CFRelease(node);
117 }
118 if (rec) {
119 *attrs = ODRecordCopyDetails(rec, NULL, &error);
120 if (*attrs) {
121 CFArrayRef values = CFDictionaryGetValue(*attrs, kODAttributeTypeMetaNodeLocation);
122 DSPath = (values && CFArrayGetCount(values) > 0) ? CFArrayGetValueAtIndex(values, 0) : NULL;
123 }
124
125 /*
126 * Prompt for a password if -u was specified,
127 * or if we are not root,
128 * or if we are updating something not on the
129 * local node.
130 */
131 if (authname || !master_mode ||
132 (DSPath && CFStringCompareWithOptions(DSPath, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo)) {
133
134 CFStringRef password = NULL;
135
136 if (!authname) authname = user;
137
138 password = prompt_passwd(authname);
139 if (!ODRecordSetNodeCredentials(rec, authname, password, &error)) {
140 CFRelease(rec);
141 rec = NULL;
142 }
143 }
144 }
145
146 if (error) show_error(error);
147 return rec;
148 }
149
150 void
151 odUpdateUser(ODRecordRef rec, CFDictionaryRef attrs_orig, CFDictionaryRef attrs)
152 {
153 CFErrorRef error = NULL;
154 int updated = 0;
155 ENTRY* ep;
156
157 for (ep = list; ep->prompt; ep++) {
158
159 // Nothing to update
160 if (!rec || !attrs_orig || !attrs) break;
161
162 // No need to update if entry is restricted
163 if (ep->restricted) continue;
164
165 CFArrayRef values_orig = CFDictionaryGetValue(attrs_orig, *ep->attrName);
166 CFTypeRef value_orig = values_orig && CFArrayGetCount(values_orig) ? CFArrayGetValueAtIndex(values_orig, 0) : NULL;
167 CFTypeRef value = CFDictionaryGetValue(attrs, *ep->attrName);
168
169 // No need to update if both values are the same
170 if (value == value_orig) continue;
171
172 // No need to update if strings are equal
173 if (value && value_orig) {
174 if (CFGetTypeID(value_orig) == CFStringGetTypeID() &&
175 CFStringCompare(value_orig, value, 0) == kCFCompareEqualTo) continue;
176 }
177
178 // No need to update if empty string replaces NULL
179 if (!value_orig && value) {
180 if (CFStringGetLength(value) == 0) continue;
181 }
182
183 // Needs update
184 if (value) {
185 // if new value is an empty string, send an empty dictionary which will delete the property.
186 CFIndex count = CFEqual(value, CFSTR("")) ? 0 : 1;
187 CFTypeRef vals[] = { value };
188 CFArrayRef values = CFArrayCreate(NULL, vals, count, &kCFTypeArrayCallBacks);
189 if (values && ODRecordSetValue(rec, *ep->attrName, values, &error)) {
190 updated = 1;
191 }
192 if (values) CFRelease(values);
193 if (error) show_error(error);
194 }
195 }
196
197 if (updated) {
198 updated = ODRecordSynchronize(rec, &error);
199 if (error) show_error(error);
200 }
201 if (!updated) {
202 fprintf(stderr, "%s: no changes made\n", progname);
203 }
204 }
205 #endif /* OPEN_DIRECTORY */