1/*
2 * Copyright (c) 2003-2004 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 * create_fv_user.c
24 */
25
26#include "create_fv_user.h"
27
28#include "readline.h"
29#include "tokenadmin.h"
30#include "TokenIDHelper.h"
31
32#include <stdio.h>
33#include <stdlib.h>
34#include <string.h>
35#include <unistd.h>
36#include <Security/SecKeychain.h>
37#include <SecurityFoundation/FileVaultPriv.h>
38#include <SecurityFoundation/SFAuthorization.h>
39#import <Foundation/NSString.h>
40#import <Foundation/NSDictionary.h>
41
42#if 0
43#import <Admin/LoginPrefs.h>
44#import <Admin/User.h>
45#import <Admin/UserAdditions.h>
46#import <Admin/AdminConst.h>
47#import <Admin/Group.h>
48#import <Admin/Authenticator.h>
49#import <Admin/DSAuthenticator.h>
50#else
51#import <SystemAdministration/SystemAdministration.h>
52#endif
53
54static int do_create_fv_user(const char *userShortName, const char *userFullName, const char *kcpassword);
55static BOOL verify_userFullName(const char *userFullName);
56static BOOL verify_userUnixName(const char *userShortName);
57static BOOL checkHomedirExistence();
58static void _createUserAccount(NSArray *inCertificates);
59static BOOL authorize_me();
60
61NSString *mNewUserFullName;
62NSString *mNewUserName;
63NSString *mNewUserNameWarn;
64NSString *mNewUserPassword;
65
66// Fix build failure. Remove this when
67//	<rdar://problem/4874550> Admin support functions for creation of token protected FileVault users
68// is submitted
69#ifndef kHomeDirectoryCertificates
70#define kHomeDirectoryCertificates @"HomeDirectoryCertificates"
71#endif
72
73SFAuthorization *mAuthorization;
74/*
75	-p optional-keychain-password
76	-u usershortname
77	-l user-long-name
78	-h hash-of-encryption-key
79*/
80
81int
82create_fv_user(int argc, char * const *argv)
83{
84	char *userShortName = NULL;
85	char *userFullName = NULL;
86	char *kcpassword = NULL;
87	char *encryptionHash = NULL;
88	int ch, result = 0;
89	while ((ch = getopt(argc, argv, "u:l:p:h:")) != -1)
90	{
91		switch  (ch)
92		{
93		case 'u':
94			userShortName = optarg;
95			break;
96		case 'l':
97			userFullName = optarg;
98			break;
99		case 'p':
100			kcpassword = optarg;
101			break;
102		case 'h':
103			encryptionHash = optarg;
104			break;
105		case '?':
106		default:
107			return 2; /* @@@ Return 2 triggers usage message. */
108		}
109	}
110	argc -= optind;
111	argv += optind;
112
113	if (argc != 0)
114		return 2;
115
116	result = do_create_fv_user(userShortName, userFullName, kcpassword);
117
118	return result;
119}
120
121static int do_create_fv_user(const char *userShortName, const char *userFullName, const char *kcpassword)
122{
123	OSStatus status;
124	CFTypeRef identityOrArray = NULL;
125	CFArrayRef wrappingCertificates = NULL;
126
127	if (!authorize_me())
128		return 1;
129
130	printf("Connecting to writeconfig...\n");
131	AdminAuthenticator *sharedAuthenticator = [AdminAuthenticator sharedAuthenticator];
132	[sharedAuthenticator authenticateUsingAuthorization:mAuthorization];
133	if (![sharedAuthenticator isAuthenticated])
134	{
135		sec_error("Unable to connect to ToolLiason");
136		return 1;
137	}
138	printf("Connected\n");
139
140	if (!SecFileVaultMasterPasswordEnabled(NULL))
141	{
142		sec_error("FileVault master password not enabled");
143		return 1;
144	}
145
146	if (!verify_userFullName(userFullName))
147		return 1;
148	if (!verify_userUnixName(userShortName))
149		return 1;
150	if (!checkHomedirExistence())
151		return 1;
152
153	NS_DURING
154		mNewUserPassword = [NSString stringWithUTF8String:kcpassword];
155	NS_HANDLER
156		NSLog(@"failed to convert string");
157	NS_ENDHANDLER
158
159	status = findEncryptionIdentities(&identityOrArray);
160	if (status)// || !identityArray || CFArrayGetCount(identityArray)==0)
161	{
162		sec_perror("Error when searching for encryption identities", status);
163		return status;
164	}
165	extractCertificatesFromIdentities(identityOrArray, &wrappingCertificates);
166	if (!wrappingCertificates || CFArrayGetCount(wrappingCertificates)==0)
167	{
168		sec_error("Error processing encryption identities");
169		return status;
170	}
171
172	// add code here to append the cert from the master fv password keychain
173
174	sec_error("Creating user \"%s\" (%s)",userFullName,userShortName);
175	// what do we do now??
176	_createUserAccount((NSArray *)wrappingCertificates);
177
178	return 0;
179}
180
181static BOOL authorize_me()
182{
183	const char *kRightName = "system.preferences.accounts";
184	AuthorizationFlags flags = kAuthorizationFlagExtendRights | kAuthorizationFlagInteractionAllowed; // XXX remove kAuthorizationFlagInteractionAllowed
185	AuthorizationItem myAction = { kRightName, 0, 0, 0 };
186	AuthorizationRights myRights = {1, &myAction};
187//	AuthorizationEnvironment myEnvironment = {0,};
188
189	printf("Authorizing right %s\n", kRightName);
190	mAuthorization = [SFAuthorization authorizationWithFlags:flags
191		rights:&myRights environment:kAuthorizationEmptyEnvironment]; //&myEnvironment
192
193	if (!mAuthorization)
194		sec_error("Unable to obtain authorization for %s", kRightName);
195
196	return (mAuthorization!=0);
197//	if (![myauth obtainWithRights:(const AuthorizationRights *)rights flags:(AuthorizationFlags)flags environment:(const AuthorizationEnvironment *)environment authorizedRights:(AuthorizationRights **)authorizedRights error:(NSError**)error;
198
199//	[self setAuthenticator:[DSAuthenticator sharedDSAuthenticator]];
200}
201
202static BOOL verify_userFullName(const char *userFullName)
203{
204	// Verify user Full name
205	// Return true if OK to use
206	printf("Validating full name: %s\n", userFullName);
207
208	if ((userFullName == 0) || (strlen(userFullName) == 0))
209	{
210		sec_error("User full name is required");
211		return NO;
212	}
213	NS_DURING
214		mNewUserFullName = [NSString stringWithUTF8String:userFullName];
215	NS_HANDLER
216		NSLog(@"failed to convert string");
217	NS_ENDHANDLER
218
219	if(([mNewUserFullName caseInsensitiveCompare:@"admin"] != NSOrderedSame) &&
220		([ADMGroup findGroupByName:mNewUserFullName] != NULL))
221	{
222		sec_error("User full name is not available (a group by that name exists)");	//USERNAME_IS_NOT_UNIQUE_SHORT
223		return NO;
224	}
225
226	if(![ADMUser isUserNameUnique:mNewUserFullName searchParent:NO])
227	{
228		sec_error("User full name is not unique");	//USERNAME_IS_NOT_UNIQUE_SHORT
229		return NO;
230	}
231	return YES;
232}
233
234static BOOL verify_userUnixName(const char *userShortName)
235{
236	// Verify unix-user name
237	// Return true if OK to use
238
239	if ((userShortName == 0) || (strlen(userShortName) == 0))
240	{
241		sec_error("User short name is required");
242		return NO;
243	}
244
245	printf("Validating short name: %s\n", userShortName);
246	NS_DURING
247		mNewUserName = [NSString stringWithUTF8String:userShortName];
248	NS_HANDLER
249		NSLog(@"failed to convert string");
250	NS_ENDHANDLER
251
252	if([mNewUserName isEqualToString:@"ftp"])
253		return NO;
254
255	if([mNewUserName isEqualToString:@"public"])
256		return NO;
257
258	if([mNewUserName length] == 0)
259		return NO;
260
261	if(![ADMUser isUserNameUnique:mNewUserName searchParent:NO])
262	{
263		sec_error("User short name is not unique");	//USERNAME_IS_NOT_UNIQUE_SHORT
264		return NO;
265	}
266
267	if(![ADMUser isUnixNameValid:mNewUserName])
268	{
269		sec_error("User short name is not a valid unix name");	//UNIXNAME_IS_NOT_VALID
270		return NO;
271	}
272
273	if(([mNewUserName caseInsensitiveCompare:@"admin"] != NSOrderedSame) &&
274		([ADMGroup findGroupByName:mNewUserName] != NULL))
275	{
276		sec_error("User short name is not available (a group by that name exists)");	//USERNAME_IS_NOT_UNIQUE_SHORT
277		return NO;
278	}
279
280	// Orig:  Fix for 3707901; Warn user if user's short name is admin
281	// We do not permit user's short name to be "admin"
282	if([mNewUserName caseInsensitiveCompare:@"admin"] == NSOrderedSame)
283	{
284		sec_error("User short name may not be \"admin\"");
285		return NO;
286	}
287
288	return YES;
289}
290
291static BOOL checkHomedirExistence()
292{
293	// Check if home already exists - we disallow
294	NSFileManager *		fm = [NSFileManager defaultManager];
295	BOOL				directory;
296
297	if([fm fileExistsAtPath:[@"/Users/" stringByAppendingPathComponent:mNewUserName] isDirectory:&directory])
298	{
299		sec_error("Home directory already exists");
300		return NO;
301	}
302
303	return YES;
304}
305
306static void _createUserAccount(NSArray *inCertificates)
307{
308	// local version of [AddRecordController _createUserAccount:]
309
310	printf("Creating new user account: %s\n", [mNewUserName UTF8String]);
311	ADMUser *		newUser = [ADMUser newUser];
312	NSDictionary *inParameters =[NSDictionary dictionaryWithObject:inCertificates
313		forKey:kHomeDirectoryCertificates];
314
315	[newUser setName:mNewUserName];
316	[newUser setHomeDirectory:[newUser defaultHomeDirectory]];
317
318	[newUser setUserCanChangePassword:YES];
319	[newUser setUserCanChangeHint:YES];
320	[newUser setUserCanChangePicture:YES];
321	[newUser setUserCanChangeFullName:YES];
322	[newUser setFullName:mNewUserFullName];
323
324	[newUser setHint:@""];
325	[newUser setPassword:mNewUserPassword];
326
327	[newUser setHomeDirEncrypted:YES];
328	[newUser commitChanges];
329//	[newUser createHomeDirectory];
330	printf("Creating home directory...\n");
331	if (![newUser createHomeDirectoryWithParameters:inParameters])
332		sec_error("Failed to create home directory");
333
334	[newUser setAdministrator:NO];
335
336	[newUser createHTTPConfig];
337	printf("New user account created and configured\n");
338//	[Utilities syncAFPIfNeeded];
339}
340//>>>>>>>>>>>> don't forget to add the hash to "dsAttrTypeStandard:AuthenticationAuthority"
341/*
342	NSArray *forbiddenUserNames = [NSArray arrayWithObjects:
343		@"admin", @"ftp", @"public",
344		nil];
345tolower
346- (NSUInteger)[forbiddenUserNames indexOfObject:(id)anObject;
347NSString
348*/
349