main
cpp 1,175 lines 37 KB
Raw
1 // Copyright (c) .NET Foundation and contributors. All rights reserved. Licensed under the Microsoft Reciprocal License. See LICENSE.TXT file in the project root for full license information.
2
3 #include "precomp.h"
4
5 /********************************************************************
6 * CreateSmb - CUSTOM ACTION ENTRY POINT for creating fileshares
7 *
8 * Input: deferred CustomActionData -
9 * wzFsKey\twzShareDesc\twzFullPath\tfIntegratedAuth\twzUserName\tnPermissions\twzUserName\tnPermissions...
10 *
11 * ****************************************************************/
12 extern "C" UINT __stdcall CreateSmb(MSIHANDLE hInstall)
13 {
14 //AssertSz(0, "debug CreateSmb");
15 UINT er = ERROR_SUCCESS;
16 HRESULT hr = S_OK;
17
18 LPWSTR pwzData = NULL;
19 LPWSTR pwz = NULL;
20 LPWSTR pwzFsKey = NULL;
21 LPWSTR pwzShareDesc = NULL;
22 LPWSTR pwzDirectory = NULL;
23 int iAccessMode = 0;
24 DWORD nExPermissions = 0;
25 BOOL fIntegratedAuth;
26 LPWSTR pwzExUser = NULL;
27 SCA_SMBP ssp = {0};
28 DWORD dwExUserPerms = 0;
29 DWORD dwCounter = 0;
30 SCA_SMBP_USER_PERMS* pUserPermsList = NULL;
31
32 hr = WcaInitialize(hInstall, "CreateSmb");
33 ExitOnFailure(hr, "failed to initialize");
34
35 hr = WcaGetProperty( L"CustomActionData", &pwzData);
36 ExitOnFailure(hr, "failed to get CustomActionData");
37
38 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
39
40 pwz = pwzData;
41 hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name
42 ExitOnFailure(hr, "failed to read share name");
43 hr = WcaReadStringFromCaData(&pwz, &pwzShareDesc); // share description
44 ExitOnFailure(hr, "failed to read share name");
45 hr = WcaReadStringFromCaData(&pwz, &pwzDirectory); // full path to share
46 ExitOnFailure(hr, "failed to read share name");
47 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&fIntegratedAuth));
48 ExitOnFailure(hr, "failed to read integrated authentication");
49
50 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&dwExUserPerms));
51 ExitOnFailure(hr, "failed to read count of permissions to set");
52 if(dwExUserPerms > 0)
53 {
54 pUserPermsList = static_cast<SCA_SMBP_USER_PERMS*>(MemAlloc(sizeof(SCA_SMBP_USER_PERMS)*dwExUserPerms, TRUE));
55 ExitOnNull(pUserPermsList, hr, E_OUTOFMEMORY, "failed to allocate memory for permissions structure");
56
57 //Pull out all of the ExUserPerm strings
58 for (dwCounter = 0; dwCounter < dwExUserPerms; ++dwCounter)
59 {
60 hr = WcaReadStringFromCaData(&pwz, &pwzExUser); // user account
61 ExitOnFailure(hr, "failed to read user account");
62 pUserPermsList[dwCounter].wzUser = pwzExUser;
63 pwzExUser = NULL;
64
65 hr = WcaReadIntegerFromCaData(&pwz, &iAccessMode);
66 ExitOnFailure(hr, "failed to read access mode");
67 pUserPermsList[dwCounter].accessMode = (ACCESS_MODE)iAccessMode;
68 iAccessMode = 0;
69
70 hr = WcaReadIntegerFromCaData(&pwz, reinterpret_cast<int *>(&nExPermissions));
71 ExitOnFailure(hr, "failed to read count of permissions");
72 pUserPermsList[dwCounter].nPermissions = nExPermissions;
73 nExPermissions = 0;
74 }
75 }
76
77 ssp.wzKey = pwzFsKey;
78 ssp.wzDescription = pwzShareDesc;
79 ssp.wzDirectory = pwzDirectory;
80 ssp.fUseIntegratedAuth = fIntegratedAuth;
81 ssp.dwUserPermissionCount = dwExUserPerms;
82 ssp.pUserPerms = pUserPermsList;
83
84 hr = ScaEnsureSmbExists(&ssp);
85 MessageExitOnFailure(hr, msierrSMBFailedCreate, "failed to create share: '%ls'", pwzFsKey);
86
87 hr = WcaProgressMessage(COST_SMB_CREATESMB, FALSE);
88
89 LExit:
90 ReleaseStr(pwzFsKey);
91 ReleaseStr(pwzShareDesc);
92 ReleaseStr(pwzDirectory);
93 ReleaseStr(pwzData);
94
95 if (pUserPermsList)
96 {
97 MemFree(pUserPermsList);
98 }
99
100 if (FAILED(hr))
101 {
102 er = ERROR_INSTALL_FAILURE;
103 }
104 return WcaFinalize(er);
105 }
106
107
108
109 /********************************************************************
110 DropSmb - CUSTOM ACTION ENTRY POINT for creating fileshares
111
112 Input: deferred CustomActionData - wzFsKey\twzShareDesc\twzFullPath\tnPermissions\tfIntegratedAuth\twzUserName\twzPassword
113
114 * ****************************************************************/
115 extern "C" UINT __stdcall DropSmb(MSIHANDLE hInstall)
116 {
117 //AssertSz(0, "debug DropSmb");
118 UINT er = ERROR_SUCCESS;
119 HRESULT hr = S_OK;
120
121 LPWSTR pwzData = NULL;
122 LPWSTR pwz = NULL;
123 LPWSTR pwzFsKey = NULL;
124 SCA_SMBP ssp = {0};
125
126 hr = WcaInitialize(hInstall, "DropSmb");
127 ExitOnFailure(hr, "failed to initialize");
128
129 hr = WcaGetProperty( L"CustomActionData", &pwzData);
130 ExitOnFailure(hr, "failed to get CustomActionData");
131
132 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
133
134 pwz = pwzData;
135 hr = WcaReadStringFromCaData(&pwz, &pwzFsKey); // share name
136 ExitOnFailure(hr, "failed to read share name");
137
138 ssp.wzKey = pwzFsKey;
139
140 hr = ScaDropSmb(&ssp);
141 MessageExitOnFailure(hr, msierrSMBFailedDrop, "failed to delete share: '%ls'", pwzFsKey);
142
143 hr = WcaProgressMessage(COST_SMB_DROPSMB, FALSE);
144
145 LExit:
146 ReleaseStr(pwzFsKey);
147 ReleaseStr(pwzData);
148
149 if (FAILED(hr))
150 {
151 er = ERROR_INSTALL_FAILURE;
152 }
153 return WcaFinalize(er);
154 }
155
156
157 static HRESULT AddUserToGroup(
158 __in LPWSTR wzUser,
159 __in LPCWSTR wzUserDomain,
160 __in LPCWSTR wzGroup,
161 __in LPCWSTR wzGroupDomain
162 )
163 {
164 Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain);
165
166 HRESULT hr = S_OK;
167 IADsGroup *pGroup = NULL;
168 BSTR bstrUser = NULL;
169 BSTR bstrGroup = NULL;
170 LPCWSTR wz = NULL;
171 LPWSTR pwzUser = NULL;
172 LOCALGROUP_MEMBERS_INFO_3 lgmi;
173
174 if (*wzGroupDomain)
175 {
176 wz = wzGroupDomain;
177 }
178
179 // Try adding it to the global group first
180 UINT ui = ::NetGroupAddUser(wz, wzGroup, wzUser);
181 if (NERR_GroupNotFound == ui)
182 {
183 // Try adding it to the local group
184 if (wzUserDomain)
185 {
186 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser);
187 ExitOnFailure(hr, "failed to allocate user domain string");
188 }
189
190 lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser);
191 ui = ::NetLocalGroupAddMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1);
192 }
193 hr = HRESULT_FROM_WIN32(ui);
194 if (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr) // if they're already a member of the group don't report an error
195 hr = S_OK;
196
197 //
198 // If we failed, try active directory
199 //
200 if (FAILED(hr))
201 {
202 WcaLog(LOGMSG_VERBOSE, "Failed to add user: %ls, domain %ls to group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr);
203
204 hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser);
205 ExitOnFailure(hr, "failed to create user ADsPath for user: %ls domain: %ls", wzUser, wzUserDomain);
206
207 hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup);
208 ExitOnFailure(hr, "failed to create group ADsPath for group: %ls domain: %ls", wzGroup, wzGroupDomain);
209
210 hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
211 ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) );
212
213 hr = pGroup->Add(bstrUser);
214 if ((HRESULT_FROM_WIN32(ERROR_OBJECT_ALREADY_EXISTS) == hr) || (HRESULT_FROM_WIN32(ERROR_MEMBER_IN_ALIAS) == hr))
215 hr = S_OK;
216
217 ExitOnFailure(hr, "Failed to add user %ls to group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
218 }
219
220 LExit:
221 ReleaseObject(pGroup);
222 ReleaseBSTR(bstrUser);
223 ReleaseBSTR(bstrGroup);
224
225 return hr;
226 }
227
228 static HRESULT RemoveUserFromGroup(
229 __in LPWSTR wzUser,
230 __in LPCWSTR wzUserDomain,
231 __in LPCWSTR wzGroup,
232 __in LPCWSTR wzGroupDomain
233 )
234 {
235 Assert(wzUser && *wzUser && wzUserDomain && wzGroup && *wzGroup && wzGroupDomain);
236
237 HRESULT hr = S_OK;
238 IADsGroup *pGroup = NULL;
239 BSTR bstrUser = NULL;
240 BSTR bstrGroup = NULL;
241 LPCWSTR wz = NULL;
242 LPWSTR pwzUser = NULL;
243 LOCALGROUP_MEMBERS_INFO_3 lgmi;
244
245 if (*wzGroupDomain)
246 {
247 wz = wzGroupDomain;
248 }
249
250 // Try removing it from the global group first
251 UINT ui = ::NetGroupDelUser(wz, wzGroup, wzUser);
252 if (NERR_GroupNotFound == ui)
253 {
254 // Try removing it from the local group
255 if (wzUserDomain)
256 {
257 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzUserDomain, wzUser);
258 ExitOnFailure(hr, "failed to allocate user domain string");
259 }
260
261 lgmi.lgrmi3_domainandname = (NULL == pwzUser ? wzUser : pwzUser);
262 ui = ::NetLocalGroupDelMembers(wz, wzGroup, 3 , reinterpret_cast<LPBYTE>(&lgmi), 1);
263 }
264 hr = HRESULT_FROM_WIN32(ui);
265
266 //
267 // If we failed, try active directory
268 //
269 if (FAILED(hr))
270 {
271 WcaLog(LOGMSG_VERBOSE, "Failed to remove user: %ls, domain %ls from group: %ls, domain: %ls with error 0x%x. Attempting to use Active Directory", wzUser, wzUserDomain, wzGroup, wzGroupDomain, hr);
272
273 hr = UserCreateADsPath(wzUserDomain, wzUser, &bstrUser);
274 ExitOnFailure(hr, "failed to create user ADsPath in order to remove user: %ls domain: %ls from a group", wzUser, wzUserDomain);
275
276 hr = UserCreateADsPath(wzGroupDomain, wzGroup, &bstrGroup);
277 ExitOnFailure(hr, "failed to create group ADsPath in order to remove user from group: %ls domain: %ls", wzGroup, wzGroupDomain);
278
279 hr = ::ADsGetObject(bstrGroup,IID_IADsGroup, reinterpret_cast<void**>(&pGroup));
280 ExitOnFailure(hr, "Failed to get group '%ls'.", reinterpret_cast<WCHAR*>(bstrGroup) );
281
282 hr = pGroup->Remove(bstrUser);
283 ExitOnFailure(hr, "Failed to remove user %ls from group '%ls'.", reinterpret_cast<WCHAR*>(bstrUser), reinterpret_cast<WCHAR*>(bstrGroup) );
284 }
285
286 LExit:
287 ReleaseObject(pGroup);
288 ReleaseBSTR(bstrUser);
289 ReleaseBSTR(bstrGroup);
290
291 return hr;
292 }
293
294
295 static HRESULT GetUserHasRight(
296 __in LSA_HANDLE hPolicy,
297 __in PSID pUserSid,
298 __in LPWSTR wzRight,
299 __out BOOL* fHasRight
300 )
301 {
302 HRESULT hr = S_OK;
303 NTSTATUS nt = 0;
304 LSA_UNICODE_STRING lucPrivilege = { 0 };
305 PLSA_ENUMERATION_INFORMATION rgSids = NULL;
306 ULONG cSids = 0;
307 *fHasRight = FALSE;
308
309 lucPrivilege.Buffer = wzRight;
310 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
311 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
312
313 nt = ::LsaEnumerateAccountsWithUserRight(hPolicy, &lucPrivilege, reinterpret_cast<PVOID*>(&rgSids), &cSids);
314 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
315 ExitOnFailure(hr, "Failed to enumerate users for right: %ls", lucPrivilege.Buffer);
316
317 for (DWORD i = 0; i < cSids; ++i)
318 {
319 PLSA_ENUMERATION_INFORMATION pInfo = rgSids + i;
320 if (::EqualSid(pUserSid, pInfo->Sid))
321 {
322 *fHasRight = TRUE;
323 break;
324 }
325 }
326
327 LExit:
328 if (rgSids)
329 {
330 ::LsaFreeMemory(rgSids);
331 }
332
333 return hr;
334 }
335
336
337 static HRESULT GetExistingUserRightsAssignments(
338 __in_opt LPCWSTR wzDomain,
339 __in LPCWSTR wzName,
340 __inout int* iAttributes
341 )
342 {
343 HRESULT hr = S_OK;
344 NTSTATUS nt = 0;
345 BOOL fHasRight = FALSE;
346
347 LSA_HANDLE hPolicy = NULL;
348 LSA_OBJECT_ATTRIBUTES objectAttributes = { 0 };
349
350 LPWSTR pwzUser = NULL;
351 PSID psid = NULL;
352
353 if (wzDomain && *wzDomain)
354 {
355 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
356 ExitOnFailure(hr, "Failed to allocate user with domain string");
357 }
358 else
359 {
360 hr = StrAllocString(&pwzUser, wzName, 0);
361 ExitOnFailure(hr, "Failed to allocate string from user name.");
362 }
363
364 hr = AclGetAccountSid(NULL, pwzUser, &psid);
365 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
366
367 nt = ::LsaOpenPolicy(NULL, &objectAttributes, POLICY_LOOKUP_NAMES | POLICY_VIEW_LOCAL_INFORMATION, &hPolicy);
368 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
369 ExitOnFailure(hr, "Failed to open LSA policy store");
370
371 hr = GetUserHasRight(hPolicy, psid, L"SeServiceLogonRight", &fHasRight);
372 ExitOnFailure(hr, "Failed to check LogonAsService right");
373
374 if (fHasRight)
375 {
376 *iAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE;
377 }
378
379 hr = GetUserHasRight(hPolicy, psid, L"SeBatchLogonRight", &fHasRight);
380 ExitOnFailure(hr, "Failed to check LogonAsBatchJob right");
381
382 if (fHasRight)
383 {
384 *iAttributes |= SCAU_ALLOW_LOGON_AS_BATCH;
385 }
386
387 LExit:
388 if (hPolicy)
389 {
390 ::LsaClose(hPolicy);
391 }
392
393 ReleaseSid(psid);
394 ReleaseStr(pwzUser);
395 return hr;
396 }
397
398
399 static HRESULT ModifyUserLocalServiceRight(
400 __in_opt LPCWSTR wzDomain,
401 __in LPCWSTR wzName,
402 __in BOOL fAdd
403 )
404 {
405 HRESULT hr = S_OK;
406 NTSTATUS nt = 0;
407
408 LPWSTR pwzUser = NULL;
409 PSID psid = NULL;
410 LSA_HANDLE hPolicy = NULL;
411 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
412 LSA_UNICODE_STRING lucPrivilege = { 0 };
413
414 if (wzDomain && *wzDomain)
415 {
416 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
417 ExitOnFailure(hr, "Failed to allocate user with domain string");
418 }
419 else
420 {
421 hr = StrAllocString(&pwzUser, wzName, 0);
422 ExitOnFailure(hr, "Failed to allocate string from user name.");
423 }
424
425 hr = AclGetAccountSid(NULL, pwzUser, &psid);
426 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
427
428 nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy);
429 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
430 ExitOnFailure(hr, "Failed to open LSA policy store.");
431
432 lucPrivilege.Buffer = L"SeServiceLogonRight";
433 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
434 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
435
436 if (fAdd)
437 {
438 nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1);
439 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
440 ExitOnFailure(hr, "Failed to add 'logon as service' bit to user: %ls", pwzUser);
441 }
442 else
443 {
444 nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1);
445 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
446 ExitOnFailure(hr, "Failed to remove 'logon as service' bit from user: %ls", pwzUser);
447 }
448
449 LExit:
450 if (hPolicy)
451 {
452 ::LsaClose(hPolicy);
453 }
454
455 ReleaseSid(psid);
456 ReleaseStr(pwzUser);
457 return hr;
458 }
459
460
461 static HRESULT ModifyUserLocalBatchRight(
462 __in_opt LPCWSTR wzDomain,
463 __in LPCWSTR wzName,
464 __in BOOL fAdd
465 )
466 {
467 HRESULT hr = S_OK;
468 NTSTATUS nt = 0;
469
470 LPWSTR pwzUser = NULL;
471 PSID psid = NULL;
472 LSA_HANDLE hPolicy = NULL;
473 LSA_OBJECT_ATTRIBUTES ObjectAttributes = { 0 };
474 LSA_UNICODE_STRING lucPrivilege = { 0 };
475
476 if (wzDomain && *wzDomain)
477 {
478 hr = StrAllocFormatted(&pwzUser, L"%s\\%s", wzDomain, wzName);
479 ExitOnFailure(hr, "Failed to allocate user with domain string");
480 }
481 else
482 {
483 hr = StrAllocString(&pwzUser, wzName, 0);
484 ExitOnFailure(hr, "Failed to allocate string from user name.");
485 }
486
487 hr = AclGetAccountSid(NULL, pwzUser, &psid);
488 ExitOnFailure(hr, "Failed to get SID for user: %ls", pwzUser);
489
490 nt = ::LsaOpenPolicy(NULL, &ObjectAttributes, POLICY_ALL_ACCESS, &hPolicy);
491 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
492 ExitOnFailure(hr, "Failed to open LSA policy store.");
493
494 lucPrivilege.Buffer = L"SeBatchLogonRight";
495 lucPrivilege.Length = static_cast<USHORT>(lstrlenW(lucPrivilege.Buffer) * sizeof(WCHAR));
496 lucPrivilege.MaximumLength = (lucPrivilege.Length + 1) * sizeof(WCHAR);
497
498 if (fAdd)
499 {
500 nt = ::LsaAddAccountRights(hPolicy, psid, &lucPrivilege, 1);
501 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
502 ExitOnFailure(hr, "Failed to add 'logon as batch job' bit to user: %ls", pwzUser);
503 }
504 else
505 {
506 nt = ::LsaRemoveAccountRights(hPolicy, psid, FALSE, &lucPrivilege, 1);
507 hr = HRESULT_FROM_WIN32(::LsaNtStatusToWinError(nt));
508 ExitOnFailure(hr, "Failed to remove 'logon as batch job' bit from user: %ls", pwzUser);
509 }
510
511 LExit:
512 if (hPolicy)
513 {
514 ::LsaClose(hPolicy);
515 }
516
517 ReleaseSid(psid);
518 ReleaseStr(pwzUser);
519 return hr;
520 }
521
522 static void ApplyAttributes(int iAttributes, DWORD* pFlags)
523 {
524 if (SCAU_DONT_EXPIRE_PASSWRD & iAttributes)
525 {
526 *pFlags |= UF_DONT_EXPIRE_PASSWD;
527 }
528 else
529 {
530 *pFlags &= ~UF_DONT_EXPIRE_PASSWD;
531 }
532
533 if (SCAU_PASSWD_CANT_CHANGE & iAttributes)
534 {
535 *pFlags |= UF_PASSWD_CANT_CHANGE;
536 }
537 else
538 {
539 *pFlags &= ~UF_PASSWD_CANT_CHANGE;
540 }
541
542 if (SCAU_DISABLE_ACCOUNT & iAttributes)
543 {
544 *pFlags |= UF_ACCOUNTDISABLE;
545 }
546 else
547 {
548 *pFlags &= ~UF_ACCOUNTDISABLE;
549 }
550
551 if (SCAU_PASSWD_CHANGE_REQD_ON_LOGIN & iAttributes) // TODO: for some reason this doesn't work
552 {
553 *pFlags |= UF_PASSWORD_EXPIRED;
554 }
555 else
556 {
557 *pFlags &= ~UF_PASSWORD_EXPIRED;
558 }
559 }
560
561 static void ApplyComment(int iAttributes, LPWSTR pwzComment, LPWSTR* ppComment)
562 {
563 if (SCAU_REMOVE_COMMENT & iAttributes)
564 {
565 *ppComment = L"";
566 }
567 else if (pwzComment && *pwzComment)
568 {
569 *ppComment = pwzComment;
570 }
571 }
572
573 static NET_API_STATUS SetUserPassword(__in LPWSTR pwzServerName, __in LPWSTR pwzName, __in LPWSTR pwzPassword)
574 {
575 NET_API_STATUS er = NERR_Success;
576 _USER_INFO_1003 userInfo1003 = { };
577
578 userInfo1003.usri1003_password = pwzPassword;
579 er = ::NetUserSetInfo(pwzServerName, pwzName, 1003, reinterpret_cast<LPBYTE>(&userInfo1003), NULL);
580 return HRESULT_FROM_WIN32(er);
581 }
582
583 static HRESULT SetUserComment(__in LPWSTR pwzServerName, __in LPWSTR pwzName, __in LPWSTR pwzComment)
584 {
585 NET_API_STATUS er = NERR_Success;
586 _USER_INFO_1007 userInfo1007 = { };
587
588 userInfo1007.usri1007_comment = pwzComment;
589 er = ::NetUserSetInfo(pwzServerName, pwzName, 1007, reinterpret_cast<LPBYTE>(&userInfo1007), NULL);
590 return HRESULT_FROM_WIN32(er);
591 }
592
593 static HRESULT SetUserFlags(__in LPWSTR pwzServerName, __in LPWSTR pwzName, __in DWORD flags)
594 {
595 NET_API_STATUS er = NERR_Success;
596 _USER_INFO_1008 userInfo1008 = { };
597
598 userInfo1008.usri1008_flags = flags;
599 er = ::NetUserSetInfo(pwzServerName, pwzName, 1008, reinterpret_cast<LPBYTE>(&userInfo1008), NULL);
600 return HRESULT_FROM_WIN32(er);
601 }
602
603 static HRESULT RemoveUserInternal(
604 LPWSTR wzGroupCaData,
605 LPWSTR wzDomain,
606 LPWSTR wzName,
607 int iAttributes
608 )
609 {
610 HRESULT hr = S_OK;
611 UINT er = ERROR_SUCCESS;
612
613 LPWSTR pwz = NULL;
614 LPWSTR pwzGroup = NULL;
615 LPWSTR pwzGroupDomain = NULL;
616 LPWSTR pwzDomainName = NULL;
617
618 //
619 // Remove the logon as service privilege.
620 //
621 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
622 {
623 hr = ModifyUserLocalServiceRight(wzDomain, wzName, FALSE);
624 if (FAILED(hr))
625 {
626 WcaLogError(hr, "Failed to remove logon as service right from user, continuing...");
627 hr = S_OK;
628 }
629 }
630
631 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
632 {
633 hr = ModifyUserLocalBatchRight(wzDomain, wzName, FALSE);
634 if (FAILED(hr))
635 {
636 WcaLogError(hr, "Failed to remove logon as batch job right from user, continuing...");
637 hr = S_OK;
638 }
639 }
640
641 //
642 // Remove the User Account if the user was created by us.
643 //
644 if (!(SCAU_DONT_CREATE_USER & iAttributes))
645 {
646 hr = GetDomainFromServerName(&pwzDomainName, wzDomain, 0);
647 ExitOnFailure(hr, "Failed to get domain to remove user from server name: %ls", wzDomain);
648
649 er = ::NetUserDel(pwzDomainName, wzName);
650 if (NERR_UserNotFound == er)
651 {
652 er = NERR_Success;
653 }
654 ExitOnFailure(hr = HRESULT_FROM_WIN32(er), "failed to delete user account: %ls", wzName);
655 }
656 else
657 {
658 //
659 // Remove the user from the groups
660 //
661 pwz = wzGroupCaData;
662 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
663 {
664 hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
665
666 if (FAILED(hr))
667 {
668 WcaLogError(hr, "failed to get domain for group: %ls, continuing anyway.", pwzGroup);
669 }
670 else
671 {
672 hr = RemoveUserFromGroup(wzName, wzDomain, pwzGroup, pwzGroupDomain);
673 if (FAILED(hr))
674 {
675 WcaLogError(hr, "failed to remove user: %ls from group %ls, continuing anyway.", wzName, pwzGroup);
676 }
677 }
678 }
679
680 if (E_NOMOREITEMS == hr) // if there are no more items, all is well
681 {
682 hr = S_OK;
683 }
684
685 ExitOnFailure(hr, "failed to get next group from which to remove user:%ls", wzName);
686 }
687
688 LExit:
689 ReleaseStr(pwzDomainName);
690 ReleaseStr(pwzGroupDomain);
691 ReleaseStr(pwzGroup);
692
693 return hr;
694 }
695
696 /********************************************************************
697 CreateUser - CUSTOM ACTION ENTRY POINT for creating users
698
699 Input: deferred CustomActionData - UserName\tDomain\tPassword\tAttributes\tGroupName\tDomain\tGroupName\tDomain...
700 * *****************************************************************/
701 extern "C" UINT __stdcall CreateUser(
702 __in MSIHANDLE hInstall
703 )
704 {
705 //AssertSz(0, "Debug CreateUser");
706
707 HRESULT hr = S_OK;
708 UINT er = ERROR_SUCCESS;
709
710 LPWSTR pwzData = NULL;
711 LPWSTR pwz = NULL;
712 LPWSTR pwzName = NULL;
713 LPWSTR pwzDomain = NULL;
714 LPWSTR pwzComment = NULL;
715 LPWSTR pwzScriptKey = NULL;
716 LPWSTR pwzPassword = NULL;
717 LPWSTR pwzGroup = NULL;
718 LPWSTR pwzGroupDomain = NULL;
719 LPWSTR pwzDomainName = NULL;
720 int iAttributes = 0;
721 BOOL fInitializedCom = FALSE;
722
723 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
724 int iOriginalAttributes = 0;
725 int iRollbackAttributes = 0;
726
727 USER_INFO_1 userInfo1;
728 USER_INFO_1* pUserInfo1 = NULL;
729 DWORD dw;
730
731 hr = WcaInitialize(hInstall, "CreateUser");
732 ExitOnFailure(hr, "failed to initialize");
733
734 hr = ::CoInitialize(NULL);
735 ExitOnFailure(hr, "failed to initialize COM");
736 fInitializedCom = TRUE;
737
738 hr = WcaGetProperty(L"CustomActionData", &pwzData);
739 ExitOnFailure(hr, "failed to get CustomActionData");
740
741 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
742
743 //
744 // Read in the CustomActionData
745 //
746 pwz = pwzData;
747 hr = WcaReadStringFromCaData(&pwz, &pwzName);
748 ExitOnFailure(hr, "failed to read user name from custom action data");
749
750 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
751 ExitOnFailure(hr, "failed to read domain from custom action data");
752
753 hr = WcaReadStringFromCaData(&pwz, &pwzComment);
754 ExitOnFailure(hr, "failed to read user comment from custom action data");
755
756 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
757 ExitOnFailure(hr, "failed to read attributes from custom action data");
758
759 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
760 ExitOnFailure(hr, "failed to read encoding key from custom action data");
761
762 hr = WcaReadStringFromCaData(&pwz, &pwzPassword);
763 ExitOnFailure(hr, "failed to read password from custom action data");
764
765 if (!(SCAU_DONT_CREATE_USER & iAttributes))
766 {
767 pUserInfo1 = &userInfo1;
768 ::ZeroMemory(pUserInfo1, sizeof(USER_INFO_1));
769 pUserInfo1->usri1_name = pwzName;
770 pUserInfo1->usri1_priv = USER_PRIV_USER;
771 pUserInfo1->usri1_flags = UF_SCRIPT;
772 pUserInfo1->usri1_home_dir = NULL;
773 pUserInfo1->usri1_comment = NULL;
774 pUserInfo1->usri1_script_path = NULL;
775
776 // Set the user's password
777 pUserInfo1->usri1_password = pwzPassword;
778
779 // Set the user's comment
780 ApplyComment(iAttributes, pwzComment, &pUserInfo1->usri1_comment);
781
782 // Set the user's flags
783 ApplyAttributes(iAttributes, &pUserInfo1->usri1_flags);
784
785 //
786 // Create the User
787 //
788 hr = GetDomainFromServerName(&pwzDomainName, pwzDomain, 0);
789 ExitOnFailure(hr, "Failed to get domain from server name: %ls", pwzDomain);
790
791 er = ::NetUserAdd(pwzDomainName, 1, reinterpret_cast<LPBYTE>(pUserInfo1), &dw);
792 if (NERR_UserExists == er)
793 {
794 if (SCAU_FAIL_IF_EXISTS & iAttributes)
795 {
796 hr = HRESULT_FROM_WIN32(er);
797 ExitOnFailure(hr, "User was not supposed to exist, but does.");
798 }
799
800 er = ERROR_SUCCESS; // Make sure that we don't report this situation as an error
801 // if we fall through the tests that follow.
802
803 if (SCAU_UPDATE_IF_EXISTS & iAttributes)
804 {
805 pUserInfo1 = NULL;
806 er = ::NetUserGetInfo(pwzDomainName, pwzName, 1, reinterpret_cast<LPBYTE*>(&pUserInfo1));
807 if (ERROR_SUCCESS == er)
808 {
809 // There is no rollback scheduled if the key is empty.
810 // Best effort to get original configuration and save it in the script so rollback can restore it.
811
812 if (*pwzScriptKey)
813 {
814 // Try to open the rollback script
815 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
816
817 if (INVALID_HANDLE_VALUE != hRollbackScript)
818 {
819 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
820 }
821 else
822 {
823 hRollbackScript = NULL;
824 hr = WcaCaScriptCreate(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, FALSE, &hRollbackScript);
825 ExitOnFailure(hr, "Failed to open rollback CustomAction script.");
826
827 iRollbackAttributes = 0;
828 hr = GetExistingUserRightsAssignments(pwzDomain, pwzName, &iOriginalAttributes);
829 if (FAILED(hr))
830 {
831 WcaLogError(hr, "failed to get existing user rights: %ls, continuing anyway.", pwzName);
832 hr = S_OK;
833 }
834 else
835 {
836 if (!(SCAU_ALLOW_LOGON_AS_SERVICE & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes))
837 {
838 iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_SERVICE;
839 }
840
841 if (!(SCAU_ALLOW_LOGON_AS_BATCH & iOriginalAttributes) && (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes))
842 {
843 iRollbackAttributes |= SCAU_ALLOW_LOGON_AS_BATCH;
844 }
845 }
846
847 hr = WcaCaScriptWriteString(hRollbackScript, pUserInfo1->usri1_comment);
848 ExitOnFailure(hr, "Failed to add rollback comment to rollback script.");
849
850 if (!pUserInfo1->usri1_comment || !*pUserInfo1->usri1_comment)
851 {
852 iRollbackAttributes |= SCAU_REMOVE_COMMENT;
853 }
854
855 hr = WcaCaScriptWriteNumber(hRollbackScript, iRollbackAttributes);
856 ExitOnFailure(hr, "Failed to add rollback attributes to rollback script.");
857
858 // Nudge the system to get all our rollback data written to disk.
859 WcaCaScriptFlush(hRollbackScript);
860 }
861 }
862 }
863
864 if (ERROR_SUCCESS == er)
865 {
866 hr = SetUserPassword(pwzDomainName, pwzName, pwzPassword);
867 if (FAILED(hr))
868 {
869 WcaLogError(hr, "failed to set user password for user %ls\\%ls, continuing anyway.", pwzDomainName, pwzName);
870 hr = S_OK;
871 }
872
873 if (SCAU_REMOVE_COMMENT & iAttributes)
874 {
875 hr = SetUserComment(pwzDomainName, pwzName, L"");
876 if (FAILED(hr))
877 {
878 WcaLogError(hr, "failed to clear user comment for user %ls\\%ls, continuing anyway.", pwzDomainName, pwzName);
879 hr = S_OK;
880 }
881 }
882 else if (pwzComment && *pwzComment)
883 {
884 hr = SetUserComment(pwzDomainName, pwzName, pwzComment);
885 if (FAILED(hr))
886 {
887 WcaLogError(hr, "failed to set user comment to %ls for user %ls\\%ls, continuing anyway.", pwzComment, pwzDomainName, pwzName);
888 hr = S_OK;
889 }
890 }
891
892 DWORD flags = pUserInfo1->usri1_flags;
893
894 ApplyAttributes(iAttributes, &flags);
895
896 hr = SetUserFlags(pwzDomainName, pwzName, flags);
897 if (FAILED(hr))
898 {
899 WcaLogError(hr, "failed to set user flags for user %ls\\%ls, continuing anyway.", pwzDomainName, pwzName);
900 hr = S_OK;
901 }
902 }
903 }
904 }
905 else if (NERR_PasswordTooShort == er || NERR_PasswordTooLong == er)
906 {
907 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreatePswd, "failed to create user: %ls due to invalid password.", pwzName);
908 }
909
910 if (ERROR_SUCCESS != er)
911 {
912 MessageExitOnFailure(hr = HRESULT_FROM_WIN32(er), msierrUSRFailedUserCreate, "failed to create user: %ls", pwzName);
913 }
914 }
915
916 if (SCAU_ALLOW_LOGON_AS_SERVICE & iAttributes)
917 {
918 hr = ModifyUserLocalServiceRight(pwzDomain, pwzName, TRUE);
919 MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as service rights to user: %ls", pwzName);
920 }
921
922 if (SCAU_ALLOW_LOGON_AS_BATCH & iAttributes)
923 {
924 hr = ModifyUserLocalBatchRight(pwzDomain, pwzName, TRUE);
925 MessageExitOnFailure(hr, msierrUSRFailedGrantLogonAsService, "Failed to grant logon as batch job rights to user: %ls", pwzName);
926 }
927
928 //
929 // Add the users to groups
930 //
931 while (S_OK == (hr = WcaReadStringFromCaData(&pwz, &pwzGroup)))
932 {
933 hr = WcaReadStringFromCaData(&pwz, &pwzGroupDomain);
934 ExitOnFailure(hr, "failed to get domain for group: %ls", pwzGroup);
935
936 WcaLog(LOGMSG_STANDARD, "Adding user %ls\\%ls to group %ls\\%ls", pwzDomain, pwzName, pwzGroupDomain, pwzGroup);
937 hr = AddUserToGroup(pwzName, pwzDomain, pwzGroup, pwzGroupDomain);
938 MessageExitOnFailure(hr, msierrUSRFailedUserGroupAdd, "failed to add user: %ls to group %ls", pwzName, pwzGroup);
939 }
940 if (E_NOMOREITEMS == hr) // if there are no more items, all is well
941 {
942 hr = S_OK;
943 }
944 ExitOnFailure(hr, "failed to get next group in which to include user: %ls", pwzName);
945
946 LExit:
947 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_PRESERVE);
948
949 if (pUserInfo1 && pUserInfo1 != &userInfo1)
950 {
951 ::NetApiBufferFree((LPVOID)pUserInfo1);
952 }
953
954 ReleaseStr(pwzData);
955 ReleaseStr(pwzName);
956 ReleaseStr(pwzDomain);
957 ReleaseStr(pwzComment);
958 ReleaseStr(pwzScriptKey);
959 ReleaseStr(pwzPassword);
960 ReleaseStr(pwzGroup);
961 ReleaseStr(pwzGroupDomain);
962 ReleaseStr(pwzDomainName)
963
964 if (fInitializedCom)
965 {
966 ::CoUninitialize();
967 }
968
969 if (SCAU_NON_VITAL & iAttributes)
970 {
971 er = ERROR_SUCCESS;
972 }
973 else if (FAILED(hr))
974 {
975 er = ERROR_INSTALL_FAILURE;
976 }
977
978 return WcaFinalize(er);
979 }
980
981
982 /********************************************************************
983 CreateUserRollback - CUSTOM ACTION ENTRY POINT for CreateUser rollback
984
985 * *****************************************************************/
986 extern "C" UINT __stdcall CreateUserRollback(
987 MSIHANDLE hInstall
988 )
989 {
990 //AssertSz(0, "Debug CreateUserRollback");
991
992 HRESULT hr = S_OK;
993 UINT er = ERROR_SUCCESS;
994
995 LPWSTR pwzData = NULL;
996 LPWSTR pwz = NULL;
997 LPWSTR pwzScriptKey = NULL;
998 LPWSTR pwzName = NULL;
999 LPWSTR pwzDomain = NULL;
1000 LPWSTR pwzComment = NULL;
1001 int iAttributes = 0;
1002 BOOL fInitializedCom = FALSE;
1003
1004 WCA_CASCRIPT_HANDLE hRollbackScript = NULL;
1005 LPWSTR pwzRollbackData = NULL;
1006 int iOriginalAttributes = 0;
1007 LPWSTR pwzOriginalComment = NULL;
1008
1009 hr = WcaInitialize(hInstall, "CreateUserRollback");
1010 ExitOnFailure(hr, "failed to initialize");
1011
1012 hr = ::CoInitialize(NULL);
1013 ExitOnFailure(hr, "failed to initialize COM");
1014 fInitializedCom = TRUE;
1015
1016 hr = WcaGetProperty(L"CustomActionData", &pwzData);
1017 ExitOnFailure(hr, "failed to get CustomActionData");
1018
1019 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
1020
1021 //
1022 // Read in the CustomActionData
1023 //
1024 pwz = pwzData;
1025 hr = WcaReadStringFromCaData(&pwz, &pwzScriptKey);
1026 ExitOnFailure(hr, "failed to read encoding key from custom action data");
1027
1028 hr = WcaReadStringFromCaData(&pwz, &pwzName);
1029 ExitOnFailure(hr, "failed to read name from custom action data");
1030
1031 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
1032 ExitOnFailure(hr, "failed to read domain from custom action data");
1033
1034 hr = WcaReadStringFromCaData(&pwz, &pwzComment);
1035 ExitOnFailure(hr, "failed to read comment from custom action data");
1036
1037 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
1038 ExitOnFailure(hr, "failed to read attributes from custom action data");
1039
1040 // Best effort to read original configuration from CreateUser.
1041 hr = WcaCaScriptOpen(WCA_ACTION_INSTALL, WCA_CASCRIPT_ROLLBACK, FALSE, pwzScriptKey, &hRollbackScript);
1042 if (FAILED(hr))
1043 {
1044 WcaLogError(hr, "Failed to open rollback CustomAction script, continuing anyway.");
1045 }
1046 else
1047 {
1048 hr = WcaCaScriptReadAsCustomActionData(hRollbackScript, &pwzRollbackData);
1049 if (FAILED(hr))
1050 {
1051 WcaLogError(hr, "Failed to read rollback script into CustomAction data, continuing anyway.");
1052 }
1053 else
1054 {
1055 WcaLog(LOGMSG_TRACEONLY, "Rollback Data: %ls", pwzRollbackData);
1056
1057 pwz = pwzRollbackData;
1058 hr = WcaReadStringFromCaData(&pwz, &pwzOriginalComment);
1059 if (FAILED(hr))
1060 {
1061 WcaLogError(hr, "failed to read comment from rollback data, continuing anyway");
1062 }
1063 else
1064 {
1065 pwzComment = pwzOriginalComment;
1066 }
1067 hr = WcaReadIntegerFromCaData(&pwz, &iOriginalAttributes);
1068 if (FAILED(hr))
1069 {
1070 WcaLogError(hr, "failed to read attributes from rollback data, continuing anyway");
1071 }
1072 else
1073 {
1074 iAttributes |= iOriginalAttributes;
1075 }
1076 }
1077 }
1078
1079 hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
1080
1081 LExit:
1082 WcaCaScriptClose(hRollbackScript, WCA_CASCRIPT_CLOSE_DELETE);
1083
1084 ReleaseStr(pwzData);
1085 ReleaseStr(pwzName);
1086 ReleaseStr(pwzDomain);
1087 ReleaseStr(pwzComment);
1088 ReleaseStr(pwzScriptKey);
1089 ReleaseStr(pwzRollbackData);
1090 ReleaseStr(pwzOriginalComment);
1091
1092 if (fInitializedCom)
1093 {
1094 ::CoUninitialize();
1095 }
1096
1097 if (FAILED(hr))
1098 {
1099 er = ERROR_INSTALL_FAILURE;
1100 }
1101
1102 return WcaFinalize(er);
1103 }
1104
1105
1106 /********************************************************************
1107 RemoveUser - CUSTOM ACTION ENTRY POINT for removing users
1108
1109 Input: deferred CustomActionData - Name\tDomain
1110 * *****************************************************************/
1111 extern "C" UINT __stdcall RemoveUser(
1112 MSIHANDLE hInstall
1113 )
1114 {
1115 //AssertSz(0, "Debug RemoveUser");
1116
1117 HRESULT hr = S_OK;
1118 UINT er = ERROR_SUCCESS;
1119
1120 LPWSTR pwzData = NULL;
1121 LPWSTR pwz = NULL;
1122 LPWSTR pwzName = NULL;
1123 LPWSTR pwzDomain = NULL;
1124 LPWSTR pwzComment = NULL;
1125 int iAttributes = 0;
1126 BOOL fInitializedCom = FALSE;
1127
1128 hr = WcaInitialize(hInstall, "RemoveUser");
1129 ExitOnFailure(hr, "failed to initialize");
1130
1131 hr = ::CoInitialize(NULL);
1132 ExitOnFailure(hr, "failed to initialize COM");
1133 fInitializedCom = TRUE;
1134
1135 hr = WcaGetProperty(L"CustomActionData", &pwzData);
1136 ExitOnFailure(hr, "failed to get CustomActionData");
1137
1138 WcaLog(LOGMSG_TRACEONLY, "CustomActionData: %ls", pwzData);
1139
1140 //
1141 // Read in the CustomActionData
1142 //
1143 pwz = pwzData;
1144 hr = WcaReadStringFromCaData(&pwz, &pwzName);
1145 ExitOnFailure(hr, "failed to read name from custom action data");
1146
1147 hr = WcaReadStringFromCaData(&pwz, &pwzDomain);
1148 ExitOnFailure(hr, "failed to read domain from custom action data");
1149
1150 hr = WcaReadStringFromCaData(&pwz, &pwzComment);
1151 ExitOnFailure(hr, "failed to read comment from custom action data");
1152
1153 hr = WcaReadIntegerFromCaData(&pwz, &iAttributes);
1154 ExitOnFailure(hr, "failed to read attributes from custom action data");
1155
1156 hr = RemoveUserInternal(pwz, pwzDomain, pwzName, iAttributes);
1157
1158 LExit:
1159 ReleaseStr(pwzData);
1160 ReleaseStr(pwzName);
1161 ReleaseStr(pwzDomain);
1162 ReleaseStr(pwzComment);
1163
1164 if (fInitializedCom)
1165 {
1166 ::CoUninitialize();
1167 }
1168
1169 if (FAILED(hr))
1170 {
1171 er = ERROR_INSTALL_FAILURE;
1172 }
1173
1174 return WcaFinalize(er);
1175 }