]>
Commit | Line | Data |
---|---|---|
34d340d7 A |
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> | |
ef8ad44b | 8 | #include <OpenDirectory/OpenDirectory.h> |
34d340d7 | 9 | #include <OpenDirectory/OpenDirectoryPriv.h> |
34d340d7 A |
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; | |
ef8ad44b | 44 | CFArrayRef values = CFDictionaryGetValue(attrs, kODAttributeTypeUserShell); |
34d340d7 A |
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); | |
8459d725 A |
64 | size_t prompt_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(prompt), kCFStringEncodingUTF8); |
65 | char* buf = malloc(prompt_size); | |
66 | CFStringGetCString(prompt, buf, prompt_size, kCFStringEncodingUTF8); | |
34d340d7 A |
67 | char* pass = getpass(buf); |
68 | result = CFStringCreateWithCString(NULL, pass, kCFStringEncodingUTF8); | |
69 | memset(pass, 0, strlen(pass)); | |
8459d725 | 70 | free(buf); |
34d340d7 A |
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 | ||
34d340d7 A |
93 | ODRecordRef |
94 | odGetUser(CFStringRef location, CFStringRef authname, CFStringRef user, CFDictionaryRef* attrs) | |
95 | { | |
34d340d7 A |
96 | ODNodeRef node = NULL; |
97 | ODRecordRef rec = NULL; | |
98 | CFErrorRef error = NULL; | |
99 | ||
100 | assert(attrs); | |
101 | ||
34d340d7 A |
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) { | |
8459d725 | 107 | node = ODNodeCreateWithName(NULL, kODSessionDefault, location, &error); |
34d340d7 | 108 | } else { |
8459d725 | 109 | node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODNodeTypeAuthentication, &error); |
34d340d7 | 110 | } |
34d340d7 | 111 | if (node) { |
ef8ad44b | 112 | CFTypeRef vals[] = { kODAttributeTypeStandardOnly }; |
34d340d7 | 113 | CFArrayRef desiredAttrs = CFArrayCreate(NULL, vals, 1, &kCFTypeArrayCallBacks); |
ef8ad44b | 114 | rec = ODNodeCopyRecord(node, kODRecordTypeUsers, user, desiredAttrs, &error); |
34d340d7 A |
115 | if (desiredAttrs) CFRelease(desiredAttrs); |
116 | CFRelease(node); | |
117 | } | |
118 | if (rec) { | |
119 | *attrs = ODRecordCopyDetails(rec, NULL, &error); | |
120 | if (*attrs) { | |
ef8ad44b | 121 | CFArrayRef values = CFDictionaryGetValue(*attrs, kODAttributeTypeMetaNodeLocation); |
34d340d7 A |
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 | ||
ef8ad44b | 165 | CFArrayRef values_orig = CFDictionaryGetValue(attrs_orig, *ep->attrName); |
34d340d7 | 166 | CFTypeRef value_orig = values_orig && CFArrayGetCount(values_orig) ? CFArrayGetValueAtIndex(values_orig, 0) : NULL; |
ef8ad44b | 167 | CFTypeRef value = CFDictionaryGetValue(attrs, *ep->attrName); |
34d340d7 A |
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); | |
ef8ad44b | 189 | if (values && ODRecordSetValue(rec, *ep->attrName, values, &error)) { |
34d340d7 A |
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 */ |