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 "passwd.h"
29
30#ifdef INFO_OPEN_DIRECTORY
31
32#include <CoreFoundation/CoreFoundation.h>
33#include <OpenDirectory/OpenDirectory.h>
34#include <OpenDirectory/OpenDirectoryPriv.h>
35
36extern char* progname;
37int master_mode;
38
39static int
40cfprintf(FILE* file, const char* format, ...) {
41		char* cstr;
42		int result = 0;
43        va_list args;
44        va_start(args, format);
45        CFStringRef formatStr = CFStringCreateWithCStringNoCopy(NULL, format, kCFStringEncodingUTF8, kCFAllocatorNull);
46		if (formatStr) {
47			CFStringRef str = CFStringCreateWithFormatAndArguments(NULL, NULL, formatStr, args);
48			if (str) {
49				size_t size = CFStringGetMaximumSizeForEncoding(CFStringGetLength(str), kCFStringEncodingUTF8) + 1;
50				va_end(args);
51				cstr = malloc(size);
52				if (cstr && CFStringGetCString(str, cstr, size, kCFStringEncodingUTF8)) {
53					result = fprintf(file, "%s", cstr);
54					free(cstr);
55				}
56				CFRelease(str);
57			}
58			CFRelease(formatStr);
59		}
60		return result;
61}
62
63static void
64show_error(CFErrorRef error) {
65	if (error) {
66		CFStringRef desc = CFErrorCopyDescription(error);
67		if (desc) {
68			cfprintf(stderr, "%s: %@", progname, desc);
69			CFRelease(desc);
70		}
71		desc = CFErrorCopyFailureReason(error);
72		if (desc) cfprintf(stderr, "  %@", desc);
73
74		desc = CFErrorCopyRecoverySuggestion(error);
75		if (desc) cfprintf(stderr, "  %@", desc);
76
77		fprintf(stderr, "\n");
78	}
79}
80
81int
82od_passwd(char* uname, char* locn, char* aname)
83{
84	int change_pass_on_self;
85	CFErrorRef error = NULL;
86	CFStringRef username = NULL;
87	CFStringRef location = NULL;
88	CFStringRef authname = NULL;
89	ODNodeRef node = NULL;
90	ODRecordRef rec = NULL;
91	CFStringRef oldpass = NULL;
92	CFStringRef newpass = NULL;
93
94	if (uname == NULL)
95		return -1;
96
97	/*
98	 * If no explicit authorization name was specified (via -u)
99	 * then default to the target user.
100	 */
101	if (!aname) {
102		aname = strdup(uname);
103	}
104
105	master_mode = (getuid() == 0);
106	change_pass_on_self = (strcmp(aname, uname) == 0);
107
108	if (locn) {
109		location = CFStringCreateWithCString(NULL, locn, kCFStringEncodingUTF8);
110	}
111
112	if (aname) {
113		authname = CFStringCreateWithCString(NULL, aname, kCFStringEncodingUTF8);
114	}
115
116	if (uname) {
117		username = CFStringCreateWithCString(NULL, uname, kCFStringEncodingUTF8);
118		if (!username) return -1;
119	}
120
121	/*
122	 * Copy the record from the specified node, or perform a search.
123	 */
124	if (location) {
125		node = ODNodeCreateWithName(NULL, kODSessionDefault, location, &error);
126	} else {
127		node = ODNodeCreateWithNodeType(NULL, kODSessionDefault, kODNodeTypeAuthentication, &error);
128	}
129
130	if (node) {
131		rec = ODNodeCopyRecord(node, kODRecordTypeUsers, username, NULL, &error );
132		CFRelease(node);
133	}
134
135	if (!rec) {
136		if (error) {
137			show_error(error);
138		} else {
139			fprintf(stderr, "%s: Unknown user name '%s'.\n", progname, uname);
140		}
141		return -1;
142	}
143
144	/*
145	 * Get the actual location.
146	 */
147	CFArrayRef values = NULL;
148	values = ODRecordCopyValues(rec, kODAttributeTypeMetaNodeLocation, &error);
149	location = (values && CFArrayGetCount(values) > 0) ? CFArrayGetValueAtIndex(values, 0) : location;
150
151	printf("Changing password for %s.\n", uname);
152
153	/*
154	 * Prompt for password if not super-user, or if changing a remote node.
155	 */
156	int needs_auth = (!master_mode || CFStringCompareWithOptions(location, CFSTR("/Local/"), CFRangeMake(0, 7), 0) != kCFCompareEqualTo);
157
158	if (needs_auth) {
159		char prompt[BUFSIZ];
160		if (change_pass_on_self) {
161			strlcpy(prompt, "Old password:", sizeof(prompt));
162		} else {
163			snprintf(prompt, sizeof(prompt), "Password for %s:", aname);
164		}
165		char *p = getpass( prompt );
166		if (p) {
167			oldpass = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
168			memset(p, 0, strlen(p));
169		}
170	}
171
172	for (;;) {
173		char *p = getpass("New password:");
174		if (p && strlen(p) > 0) {
175			newpass = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
176			memset(p, 0, strlen(p));
177		} else {
178			printf("Password unchanged.\n");
179			exit(0);
180		}
181
182		p = getpass("Retype new password:");
183		if (p) {
184			CFStringRef verify = CFStringCreateWithCString(NULL, p, kCFStringEncodingUTF8);
185			if (!verify || !CFEqual(newpass, verify)) {
186				if (verify) CFRelease(verify);
187				printf("Mismatch; try again, EOF to quit.\n");
188			} else {
189				CFRelease(verify);
190				break;
191			}
192		}
193	}
194
195	ODRecordChangePassword(rec, oldpass, newpass, &error);
196
197	if (error) {
198		show_error(error);
199		exit(1);
200	}
201
202	if (oldpass) CFRelease(oldpass);
203	if (newpass) CFRelease(newpass);
204
205#if 0
206	if ( status != eDSNoErr ) {
207		switch( status )
208		{
209			case eDSAuthPasswordTooShort:
210				errMsgStr = "The new password is too short.";
211				break;
212
213			case eDSAuthPasswordTooLong:
214				errMsgStr = "The new password is too long.";
215				break;
216
217			case eDSAuthPasswordNeedsLetter:
218				errMsgStr = "The new password must contain a letter.";
219				break;
220
221			case eDSAuthPasswordNeedsDigit:
222				errMsgStr = "The new password must contain a number.";
223				break;
224
225			default:
226				errMsgStr = "Sorry";
227		}
228		fprintf(stderr, "%s\n", errMsgStr);
229		exit(1);
230#endif
231	return 0;
232}
233
234#endif /* INFO_OPEN_DIRECTORY */
235