#ifdef OPEN_DIRECTORY
#include "open_directory.h"
#include "chpass.h"
#include <err.h>
#include <sys/time.h>
#include <unistd.h>
#include <sys/sysctl.h>
#include <OpenDirectory/OpenDirectory.h>
#include <OpenDirectory/OpenDirectoryPriv.h>
void
setrestricted(CFDictionaryRef attrs)
{
const char* user_allowed[] = { "shell", "full name", "office location", "office phone", "home phone", "picture", NULL };
const char* root_restricted[] = { "password", "change", "expire", "class", NULL };
ENTRY* ep;
const char** pp;
int restrict_by_default = !master_mode;
for (ep = list; ep->prompt; ep++) {
ep->restricted = restrict_by_default;
pp = restrict_by_default ? user_allowed : root_restricted;
for (; *pp; pp++) {
if (strncasecmp(ep->prompt, *pp, ep->len) == 0) {
ep->restricted = !restrict_by_default;
break;
}
}
if (restrict_by_default && strcmp(ep->prompt, "shell") == 0) {
ep->restricted = 1;
CFArrayRef values = CFDictionaryGetValue(attrs, kODAttributeTypeUserShell);
CFTypeRef value = values && CFArrayGetCount(values) > 0 ? CFArrayGetValueAtIndex(values, 0) : NULL;
if (value && CFGetTypeID(value) == CFStringGetTypeID()) {
size_t size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(value), kCFStringEncodingUTF8)+1;
char* shell = malloc(size);
if (CFStringGetCString(value, shell, size, kCFStringEncodingUTF8)) {
if (ok_shell(shell)) {
ep->restricted = 0;
}
}
}
}
}
}
static CFStringRef
prompt_passwd(CFStringRef user)
{
CFStringRef result = NULL;
CFStringRef prompt = CFStringCreateWithFormat(NULL, NULL, CFSTR("Password for %@: "), user);
size_t prompt_size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(prompt), kCFStringEncodingUTF8);
char* buf = malloc(prompt_size);
CFStringGetCString(prompt, buf, prompt_size, kCFStringEncodingUTF8);
char* pass = getpass(buf);
result = CFStringCreateWithCString(NULL, pass, kCFStringEncodingUTF8);
memset(pass, 0, strlen(pass));
free(buf);
CFRelease(prompt);
return result;
}
static void
show_error(CFErrorRef error) {
if (error) {
CFStringRef desc = CFErrorCopyDescription(error);
if (desc) {
cfprintf(stderr, "%s: %@", progname, desc);
CFRelease(desc);
}
desc = CFErrorCopyFailureReason(error);
if (desc) cfprintf(stderr, " %@", desc);
desc = CFErrorCopyRecoverySuggestion(error);
if (desc) cfprintf(stderr, " %@", desc);
fprintf(stderr, "\n");
}
}
ODRecordRef
odGetUser(CFStringRef location, CFStringRef authname, CFStringRef user, CFDictionaryRef* attrs)
{
ODNodeRef node = NULL;
ODRecordRef rec = NULL;
CFErrorRef error = NULL;
assert(attrs);
if (location) {
node = ODNodeCreateWithName(NULL, kODSessionDefault, location, &error);
} else {
node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODNodeTypeAuthentication, &error);
}
if (node) {
CFTypeRef vals[] = { kODAttributeTypeStandardOnly };
CFArrayRef desiredAttrs = CFArrayCreate(NULL, vals, 1, &kCFTypeArrayCallBacks);
rec = ODNodeCopyRecord(node, kODRecordTypeUsers, user, desiredAttrs, &error);
if (desiredAttrs) CFRelease(desiredAttrs);
CFRelease(node);
}
if (rec) {
*attrs = ODRecordCopyDetails(rec, NULL, &error);
if (*attrs) {
CFArrayRef values = CFDictionaryGetValue(*attrs, kODAttributeTypeMetaNodeLocation);
DSPath = (values && CFArrayGetCount(values) > 0) ? CFArrayGetValueAtIndex(values, 0) : NULL;
}
if (authname || !master_mode ||
(DSPath && CFStringCompareWithOptions(DSPath, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo)) {
CFStringRef password = NULL;
if (!authname) authname = user;
password = prompt_passwd(authname);
if (!ODRecordSetNodeCredentials(rec, authname, password, &error)) {
CFRelease(rec);
rec = NULL;
}
}
}
if (error) show_error(error);
return rec;
}
void
odUpdateUser(ODRecordRef rec, CFDictionaryRef attrs_orig, CFDictionaryRef attrs)
{
CFErrorRef error = NULL;
int updated = 0;
ENTRY* ep;
for (ep = list; ep->prompt; ep++) {
if (!rec || !attrs_orig || !attrs) break;
if (ep->restricted) continue;
CFArrayRef values_orig = CFDictionaryGetValue(attrs_orig, *ep->attrName);
CFTypeRef value_orig = values_orig && CFArrayGetCount(values_orig) ? CFArrayGetValueAtIndex(values_orig, 0) : NULL;
CFTypeRef value = CFDictionaryGetValue(attrs, *ep->attrName);
if (value == value_orig) continue;
if (value && value_orig) {
if (CFGetTypeID(value_orig) == CFStringGetTypeID() &&
CFStringCompare(value_orig, value, 0) == kCFCompareEqualTo) continue;
}
if (!value_orig && value) {
if (CFStringGetLength(value) == 0) continue;
}
if (value) {
CFIndex count = CFEqual(value, CFSTR("")) ? 0 : 1;
CFTypeRef vals[] = { value };
CFArrayRef values = CFArrayCreate(NULL, vals, count, &kCFTypeArrayCallBacks);
if (values && ODRecordSetValue(rec, *ep->attrName, values, &error)) {
updated = 1;
}
if (values) CFRelease(values);
if (error) show_error(error);
}
}
if (updated) {
updated = ODRecordSynchronize(rec, &error);
if (error) show_error(error);
}
if (!updated) {
fprintf(stderr, "%s: no changes made\n", progname);
}
}
#endif