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 *---------------------------------------------------------------------------*/ 14void 15setrestricted(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 59static CFStringRef 60prompt_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 75static void 76show_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 93ODRecordRef 94odGetUser(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 150void 151odUpdateUser(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 */ 206