]> git.saurik.com Git - apple/system_cmds.git/blob - passwd.tproj/od_passwd.c
system_cmds-433.8.tar.gz
[apple/system_cmds.git] / passwd.tproj / od_passwd.c
1 /*
2 * Copyright (c) 1999-2006 Apple Computer, Inc. All rights reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
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
11 * file.
12 *
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.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 */
23 #include <stdio.h>
24 #include <unistd.h>
25 #include <pwd.h>
26 #include <sys/sysctl.h>
27
28 #include <CoreFoundation/CoreFoundation.h>
29 #include <OpenDirectory/OpenDirectory.h>
30 #include <OpenDirectory/OpenDirectoryPriv.h>
31 #include <DirectoryService/DirServicesTypes.h>
32
33 extern char* progname;
34 int master_mode;
35
36 static int
37 cfprintf(FILE* file, const char* format, ...) {
38 char* cstr;
39 int result = 0;
40 va_list args;
41 va_start(args, format);
42 CFStringRef formatStr = CFStringCreateWithCStringNoCopy(NULL, format, kCFStringEncodingUTF8, kCFAllocatorNull);
43 if (formatStr) {
44 CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, formatStr, args);
45 if (str) {
46 size_t size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1;
47 va_end(args);
48 cstr = malloc(size);
49 if (cstr && CFStringGetCString(str, cstr, size, kCFStringEncodingUTF8)) {
50 result = fprintf(file, "%s", cstr);
51 free(cstr);
52 }
53 CFRelease(str);
54 }
55 CFRelease(formatStr);
56 }
57 return result;
58 }
59
60 static void
61 show_error(CFErrorRef error) {
62 if (error) {
63 CFStringRef desc = CFErrorCopyDescription(error);
64 if (desc) {
65 cfprintf(stderr, "%s: %@", progname, desc);
66 CFRelease(desc);
67 }
68 desc = CFErrorCopyFailureReason(error);
69 if (desc) cfprintf(stderr, " %@", desc);
70
71 desc = CFErrorCopyRecoverySuggestion(error);
72 if (desc) cfprintf(stderr, " %@", desc);
73
74 fprintf(stderr, "\n");
75 }
76 }
77
78 static int
79 is_singleuser(void) {
80 uint32_t su = 0;
81 size_t susz = sizeof(su);
82 if (sysctlbyname("kern.singleuser", &su, &susz, NULL, 0) != 0) {
83 return 0;
84 } else {
85 return (int)su;
86 }
87 }
88
89 static int
90 load_DirectoryServicesLocal() {
91 const char* launchctl = "/bin/launchctl";
92 const char* plist = "/System/Library/LaunchDaemons/com.apple.DirectoryServicesLocal.plist";
93
94 pid_t pid = fork();
95 int status, res;
96 switch (pid) {
97 case -1: // ERROR
98 perror("launchctl");
99 return 0;
100 case 0: // CHILD
101 execl(launchctl, launchctl, "load", plist, NULL);
102 /* NOT REACHED */
103 perror("launchctl");
104 exit(1);
105 break;
106 default: // PARENT
107 do {
108 res = waitpid(pid, &status, 0);
109 } while (res == -1 && errno == EINTR);
110 if (res == -1) {
111 perror("launchctl");
112 return 0;
113 }
114 break;
115 }
116 return (WIFEXITED(status) && (WEXITSTATUS(status) == EXIT_SUCCESS));
117 }
118
119 int
120 od_passwd(char* uname, char* locn, char* aname)
121 {
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;
132
133 if (uname == NULL)
134 return -1;
135
136 /*
137 * If no explicit authorization name was specified (via -u)
138 * then default to the target user.
139 */
140 if (!aname) {
141 aname = strdup(uname);
142 }
143
144 master_mode = (getuid() == 0);
145 change_pass_on_self = (strcmp(aname, uname) == 0);
146
147 if (locn) {
148 location = CFStringCreateWithCString(NULL, locn, kCFStringEncodingUTF8);
149 }
150
151 if (aname) {
152 authname = CFStringCreateWithCString(NULL, aname, kCFStringEncodingUTF8);
153 }
154
155 if (uname) {
156 username = CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8);
157 if (!username) return -1;
158 }
159
160 /*
161 * Connect to DS server
162 */
163 session = ODSessionCreate(NULL, NULL, &error);
164 if ( !session && error && CFErrorGetCode(error) == eServerNotRunning ) {
165 /*
166 * In single-user mode, attempt to load the local DS daemon.
167 */
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);
172 if (opts) {
173 session = ODSessionCreate(NULL, opts, &error);
174 CFRelease(opts);
175 }
176
177 if (!location) {
178 location = CFRetain(CFSTR("/Local/Default"));
179 }
180 } else {
181 show_error(error);
182 return -1;
183 }
184 }
185
186
187 /*
188 * Copy the record from the specified node, or perform a search.
189 */
190 if (location) {
191 node = ODNodeCreateWithName(NULL, session, location, &error);
192 } else {
193 node = ODNodeCreateWithNodeType(NULL, session, kODTypeAuthenticationSearchNode, &error);
194 }
195
196 if (session) CFRelease(session);
197
198 if (node) {
199 rec = ODNodeCopyRecord(node, CFSTR(kDSStdRecordTypeUsers), username, NULL, &error );
200 CFRelease(node);
201 }
202
203 if (!rec) {
204 if (error) {
205 show_error(error);
206 } else {
207 fprintf(stderr, "%s: Unknown user name '%s'.\n", progname, uname);
208 }
209 return -1;
210 }
211
212 /*
213 * Get the actual location.
214 */
215 CFArrayRef values = NULL;
216 values = ODRecordCopyValues(rec, CFSTR(kDSNAttrMetaNodeLocation), &error);
217 location = (values && CFArrayGetCount(values) > 0) ? CFArrayGetValueAtIndex(values, 0) : location;
218
219 printf("Changing password for %s.\n", uname);
220
221 /*
222 * Prompt for password if not super-user, or if changing a remote node.
223 */
224 int needs_auth = (!master_mode || CFStringCompareWithOptions(location, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo);
225
226 if (needs_auth) {
227 char prompt[BUFSIZ];
228 if (change_pass_on_self) {
229 strlcpy(prompt, "Old password:", sizeof(prompt));
230 } else {
231 snprintf(prompt, sizeof(prompt), "Password for %s:", aname);
232 }
233 char *p = getpass( prompt );
234 if (p) {
235 oldpass = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
236 memset(p, 0, strlen(p));
237 }
238 }
239
240 for (;;) {
241 char *p = getpass("New password:");
242 if (p && strlen(p) > 0) {
243 newpass = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
244 memset(p, 0, strlen(p));
245 } else {
246 printf("Password unchanged.\n");
247 exit(0);
248 }
249
250 p = getpass("Retype new password:");
251 if (p) {
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");
256 } else {
257 CFRelease(verify);
258 break;
259 }
260 }
261 }
262
263 if (needs_auth) {
264 CFTypeRef values[] = { username, newpass, authname, oldpass };
265 CFArrayRef authItems = CFArrayCreate(NULL, values, 4, &kCFTypeArrayCallBacks);
266
267 ODRecordSetNodeCredentialsExtended(rec,
268 CFSTR(kDSStdRecordTypeUsers),
269 CFSTR(kDSStdAuthSetPasswd),
270 authItems,
271 NULL,
272 NULL,
273 &error);
274
275 CFRelease(authItems);
276 } else {
277 ODRecordChangePassword(rec, oldpass, newpass, &error);
278 }
279
280 if (error) {
281 show_error(error);
282 exit(1);
283 }
284
285 if (oldpass) CFRelease(oldpass);
286 if (newpass) CFRelease(newpass);
287
288 #if 0
289 if ( status != eDSNoErr ) {
290 switch( status )
291 {
292 case eDSAuthPasswordTooShort:
293 errMsgStr = "The new password is too short.";
294 break;
295
296 case eDSAuthPasswordTooLong:
297 errMsgStr = "The new password is too long.";
298 break;
299
300 case eDSAuthPasswordNeedsLetter:
301 errMsgStr = "The new password must contain a letter.";
302 break;
303
304 case eDSAuthPasswordNeedsDigit:
305 errMsgStr = "The new password must contain a number.";
306 break;
307
308 default:
309 errMsgStr = "Sorry";
310 }
311 fprintf(stderr, "%s\n", errMsgStr);
312 exit(1);
313 #endif
314 return 0;
315 }