2 * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved.
4 * @APPLE_LICENSE_HEADER_START@
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
21 * @APPLE_LICENSE_HEADER_END@
26 #include <sys/sysctl.h>
28 #include <CoreFoundation/CoreFoundation.h>
29 #include <OpenDirectory/OpenDirectory.h>
30 #include <OpenDirectory/OpenDirectoryPriv.h>
31 #include <DirectoryService/DirServicesTypes.h>
33 extern char* progname
;
37 cfprintf(FILE* file
, const char* format
, ...) {
41 va_start(args
, format
);
42 CFStringRef formatStr
= CFStringCreateWithCStringNoCopy(NULL
, format
, kCFStringEncodingUTF8
, kCFAllocatorNull
);
44 CFStringRef str
= CFStringCreateWithFormatAndArguments(NULL
, NULL
, formatStr
, args
);
46 size_t size
= CFStringGetMaximumSizeForEncoding(CFStringGetLength(str
), kCFStringEncodingUTF8
) + 1;
49 if (cstr
&& CFStringGetCString(str
, cstr
, size
, kCFStringEncodingUTF8
)) {
50 result
= fprintf(file
, "%s", cstr
);
61 show_error(CFErrorRef error
) {
63 CFStringRef desc
= CFErrorCopyDescription(error
);
65 cfprintf(stderr
, "%s: %@", progname
, desc
);
68 desc
= CFErrorCopyFailureReason(error
);
69 if (desc
) cfprintf(stderr
, " %@", desc
);
71 desc
= CFErrorCopyRecoverySuggestion(error
);
72 if (desc
) cfprintf(stderr
, " %@", desc
);
74 fprintf(stderr
, "\n");
81 size_t susz
= sizeof(su
);
82 if (sysctlbyname("kern.singleuser", &su
, &susz
, NULL
, 0) != 0) {
90 load_DirectoryServicesLocal() {
91 const char* launchctl
= "/bin/launchctl";
92 const char* plist
= "/System/Library/LaunchDaemons/com.apple.DirectoryServicesLocal.plist";
101 execl(launchctl
, launchctl
, "load", plist
, NULL
);
108 res
= waitpid(pid
, &status
, 0);
109 } while (res
== -1 && errno
== EINTR
);
116 return (WIFEXITED(status
) && (WEXITSTATUS(status
) == EXIT_SUCCESS
));
120 od_passwd(char* uname
, char* locn
, char* aname
)
122 int change_pass_on_self
;
123 CFErrorRef error
= NULL
;
124 CFStringRef username
= NULL
;
125 CFStringRef location
= NULL
;
126 CFStringRef authname
= NULL
;
127 ODSessionRef session
= NULL
;
128 ODNodeRef node
= NULL
;
129 ODRecordRef rec
= NULL
;
130 CFStringRef oldpass
= NULL
;
131 CFStringRef newpass
= NULL
;
137 * If no explicit authorization name was specified (via -u)
138 * then default to the target user.
141 aname
= strdup(uname
);
144 master_mode
= (getuid() == 0);
145 change_pass_on_self
= (strcmp(aname
, uname
) == 0);
148 location
= CFStringCreateWithCString(NULL
, locn
, kCFStringEncodingUTF8
);
152 authname
= CFStringCreateWithCString(NULL
, aname
, kCFStringEncodingUTF8
);
156 username
= CFStringCreateWithCString(NULL
, uname
, kCFStringEncodingUTF8
);
157 if (!username
) return -1;
161 * Connect to DS server
163 session
= ODSessionCreate(NULL
, NULL
, &error
);
164 if ( !session
&& error
&& CFErrorGetCode(error
) == eServerNotRunning
) {
166 * In single-user mode, attempt to load the local DS daemon.
168 if (is_singleuser() && load_DirectoryServicesLocal()) {
169 CFTypeRef keys
[] = { kODSessionLocalPath
};
170 CFTypeRef vals
[] = { CFSTR("/var/db/dslocal") };
171 CFDictionaryRef opts
= CFDictionaryCreate(NULL
, keys
, vals
, 1, &kCFTypeDictionaryKeyCallBacks
, &kCFTypeDictionaryValueCallBacks
);
173 session
= ODSessionCreate(NULL
, opts
, &error
);
178 location
= CFRetain(CFSTR("/Local/Default"));
188 * Copy the record from the specified node, or perform a search.
191 node
= ODNodeCreateWithName(NULL
, session
, location
, &error
);
193 node
= ODNodeCreateWithNodeType(NULL
, session
, kODTypeAuthenticationSearchNode
, &error
);
196 if (session
) CFRelease(session
);
199 rec
= ODNodeCopyRecord(node
, CFSTR(kDSStdRecordTypeUsers
), username
, NULL
, &error
);
207 fprintf(stderr
, "%s: Unknown user name '%s'.\n", progname
, uname
);
213 * Get the actual location.
215 CFArrayRef values
= NULL
;
216 values
= ODRecordCopyValues(rec
, CFSTR(kDSNAttrMetaNodeLocation
), &error
);
217 location
= (values
&& CFArrayGetCount(values
) > 0) ? CFArrayGetValueAtIndex(values
, 0) : location
;
219 printf("Changing password for %s.\n", uname
);
222 * Prompt for password if not super-user, or if changing a remote node.
224 int needs_auth
= (!master_mode
|| CFStringCompareWithOptions(location
, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo
);
228 if (change_pass_on_self
) {
229 strlcpy(prompt
, "Old password:", sizeof(prompt
));
231 snprintf(prompt
, sizeof(prompt
), "Password for %s:", aname
);
233 char *p
= getpass( prompt
);
235 oldpass
= CFStringCreateWithCString(NULL
, p
, kCFStringEncodingUTF8
);
236 memset(p
, 0, strlen(p
));
241 char *p
= getpass("New password:");
242 if (p
&& strlen(p
) > 0) {
243 newpass
= CFStringCreateWithCString(NULL
, p
, kCFStringEncodingUTF8
);
244 memset(p
, 0, strlen(p
));
246 printf("Password unchanged.\n");
250 p
= getpass("Retype new password:");
252 CFStringRef verify
= CFStringCreateWithCString(NULL
, p
, kCFStringEncodingUTF8
);
253 if (!verify
|| !CFEqual(newpass
, verify
)) {
254 if (verify
) CFRelease(verify
);
255 printf("Mismatch; try again, EOF to quit.\n");
264 CFTypeRef values
[] = { username
, newpass
, authname
, oldpass
};
265 CFArrayRef authItems
= CFArrayCreate(NULL
, values
, 4, &kCFTypeArrayCallBacks
);
267 ODRecordSetNodeCredentialsExtended(rec
,
268 CFSTR(kDSStdRecordTypeUsers
),
269 CFSTR(kDSStdAuthSetPasswd
),
275 CFRelease(authItems
);
277 ODRecordChangePassword(rec
, oldpass
, newpass
, &error
);
285 if (oldpass
) CFRelease(oldpass
);
286 if (newpass
) CFRelease(newpass
);
289 if ( status
!= eDSNoErr
) {
292 case eDSAuthPasswordTooShort
:
293 errMsgStr
= "The new password is too short.";
296 case eDSAuthPasswordTooLong
:
297 errMsgStr
= "The new password is too long.";
300 case eDSAuthPasswordNeedsLetter
:
301 errMsgStr
= "The new password must contain a letter.";
304 case eDSAuthPasswordNeedsDigit
:
305 errMsgStr
= "The new password must contain a number.";
311 fprintf(stderr
, "%s\n", errMsgStr
);