1 /*
2 * PROJECT: Local Security Authority Server DLL
3 * LICENSE: GPL - See COPYING in the top level directory
4 * FILE: dll/win32/samsrv/user.c
5 * PURPOSE: User specific helper functions
6 * COPYRIGHT: Copyright 2013 Eric Kohl
7 */
8
9 #include "samsrv.h"
10
11 /* FUNCTIONS ***************************************************************/
12
13 NTSTATUS
SampOpenUserObject(IN PSAM_DB_OBJECT DomainObject,IN ULONG UserId,IN ACCESS_MASK DesiredAccess,OUT PSAM_DB_OBJECT * UserObject)14 SampOpenUserObject(IN PSAM_DB_OBJECT DomainObject,
15 IN ULONG UserId,
16 IN ACCESS_MASK DesiredAccess,
17 OUT PSAM_DB_OBJECT *UserObject)
18 {
19 WCHAR szRid[9];
20
21 TRACE("(%p %lu %lx %p)\n",
22 DomainObject, UserId, DesiredAccess, UserObject);
23
24 /* Convert the RID into a string (hex) */
25 swprintf(szRid, L"%08lX", UserId);
26
27 /* Create the user object */
28 return SampOpenDbObject(DomainObject,
29 L"Users",
30 szRid,
31 UserId,
32 SamDbUserObject,
33 DesiredAccess,
34 UserObject);
35 }
36
37
38 NTSTATUS
SampAddGroupMembershipToUser(IN PSAM_DB_OBJECT UserObject,IN ULONG GroupId,IN ULONG Attributes)39 SampAddGroupMembershipToUser(IN PSAM_DB_OBJECT UserObject,
40 IN ULONG GroupId,
41 IN ULONG Attributes)
42 {
43 PGROUP_MEMBERSHIP GroupsBuffer = NULL;
44 ULONG GroupsCount = 0;
45 ULONG Length = 0;
46 ULONG i;
47 NTSTATUS Status;
48
49 TRACE("(%p %lu %lx)\n",
50 UserObject, GroupId, Attributes);
51
52 Status = SampGetObjectAttribute(UserObject,
53 L"Groups",
54 NULL,
55 NULL,
56 &Length);
57 if (!NT_SUCCESS(Status) && Status != STATUS_OBJECT_NAME_NOT_FOUND)
58 goto done;
59
60 GroupsBuffer = midl_user_allocate(Length + sizeof(GROUP_MEMBERSHIP));
61 if (GroupsBuffer == NULL)
62 {
63 Status = STATUS_INSUFFICIENT_RESOURCES;
64 goto done;
65 }
66
67 if (Status != STATUS_OBJECT_NAME_NOT_FOUND)
68 {
69 Status = SampGetObjectAttribute(UserObject,
70 L"Groups",
71 NULL,
72 GroupsBuffer,
73 &Length);
74 if (!NT_SUCCESS(Status))
75 goto done;
76
77 GroupsCount = Length / sizeof(GROUP_MEMBERSHIP);
78 }
79
80 for (i = 0; i < GroupsCount; i++)
81 {
82 if (GroupsBuffer[i].RelativeId == GroupId)
83 {
84 Status = STATUS_MEMBER_IN_GROUP;
85 goto done;
86 }
87 }
88
89 GroupsBuffer[GroupsCount].RelativeId = GroupId;
90 GroupsBuffer[GroupsCount].Attributes = Attributes;
91 Length += sizeof(GROUP_MEMBERSHIP);
92
93 Status = SampSetObjectAttribute(UserObject,
94 L"Groups",
95 REG_BINARY,
96 GroupsBuffer,
97 Length);
98
99 done:
100 if (GroupsBuffer != NULL)
101 midl_user_free(GroupsBuffer);
102
103 return Status;
104 }
105
106
107 NTSTATUS
SampRemoveGroupMembershipFromUser(IN PSAM_DB_OBJECT UserObject,IN ULONG GroupId)108 SampRemoveGroupMembershipFromUser(IN PSAM_DB_OBJECT UserObject,
109 IN ULONG GroupId)
110 {
111 PGROUP_MEMBERSHIP GroupsBuffer = NULL;
112 ULONG GroupsCount = 0;
113 ULONG Length = 0;
114 ULONG i;
115 NTSTATUS Status = STATUS_SUCCESS;
116
117 TRACE("(%p %lu)\n",
118 UserObject, GroupId);
119
120 SampGetObjectAttribute(UserObject,
121 L"Groups",
122 NULL,
123 NULL,
124 &Length);
125
126 if (Length == 0)
127 return STATUS_MEMBER_NOT_IN_GROUP;
128
129 GroupsBuffer = midl_user_allocate(Length);
130 if (GroupsBuffer == NULL)
131 {
132 Status = STATUS_INSUFFICIENT_RESOURCES;
133 goto done;
134 }
135
136 Status = SampGetObjectAttribute(UserObject,
137 L"Groups",
138 NULL,
139 GroupsBuffer,
140 &Length);
141 if (!NT_SUCCESS(Status))
142 goto done;
143
144 Status = STATUS_MEMBER_NOT_IN_GROUP;
145
146 GroupsCount = Length / sizeof(GROUP_MEMBERSHIP);
147 for (i = 0; i < GroupsCount; i++)
148 {
149 if (GroupsBuffer[i].RelativeId == GroupId)
150 {
151 Length -= sizeof(GROUP_MEMBERSHIP);
152 Status = STATUS_SUCCESS;
153
154 if (GroupsCount - i - 1 > 0)
155 {
156 CopyMemory(&GroupsBuffer[i],
157 &GroupsBuffer[i + 1],
158 (GroupsCount - i - 1) * sizeof(GROUP_MEMBERSHIP));
159 }
160
161 break;
162 }
163 }
164
165 if (!NT_SUCCESS(Status))
166 goto done;
167
168 Status = SampSetObjectAttribute(UserObject,
169 L"Groups",
170 REG_BINARY,
171 GroupsBuffer,
172 Length);
173
174 done:
175 if (GroupsBuffer != NULL)
176 midl_user_free(GroupsBuffer);
177
178 return Status;
179 }
180
181
182 NTSTATUS
SampGetUserGroupAttributes(IN PSAM_DB_OBJECT DomainObject,IN ULONG UserId,IN ULONG GroupId,OUT PULONG GroupAttributes)183 SampGetUserGroupAttributes(IN PSAM_DB_OBJECT DomainObject,
184 IN ULONG UserId,
185 IN ULONG GroupId,
186 OUT PULONG GroupAttributes)
187 {
188 PSAM_DB_OBJECT UserObject = NULL;
189 PGROUP_MEMBERSHIP GroupsBuffer = NULL;
190 ULONG Length = 0;
191 ULONG i;
192 NTSTATUS Status;
193
194 Status = SampOpenUserObject(DomainObject,
195 UserId,
196 0,
197 &UserObject);
198 if (!NT_SUCCESS(Status))
199 {
200 return Status;
201 }
202
203 SampGetObjectAttribute(UserObject,
204 L"Groups",
205 NULL,
206 NULL,
207 &Length);
208
209 if (Length == 0)
210 return STATUS_UNSUCCESSFUL; /* FIXME */
211
212 GroupsBuffer = midl_user_allocate(Length);
213 if (GroupsBuffer == NULL)
214 {
215 Status = STATUS_INSUFFICIENT_RESOURCES;
216 goto done;
217 }
218
219 Status = SampGetObjectAttribute(UserObject,
220 L"Groups",
221 NULL,
222 GroupsBuffer,
223 &Length);
224 if (!NT_SUCCESS(Status))
225 goto done;
226
227 for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++)
228 {
229 if (GroupsBuffer[i].RelativeId == GroupId)
230 {
231 *GroupAttributes = GroupsBuffer[i].Attributes;
232 goto done;
233 }
234 }
235
236 done:
237 if (GroupsBuffer != NULL)
238 midl_user_free(GroupsBuffer);
239
240 if (UserObject != NULL)
241 SampCloseDbObject(UserObject);
242
243 return Status;
244 }
245
246
247 NTSTATUS
SampSetUserGroupAttributes(IN PSAM_DB_OBJECT DomainObject,IN ULONG UserId,IN ULONG GroupId,IN ULONG GroupAttributes)248 SampSetUserGroupAttributes(IN PSAM_DB_OBJECT DomainObject,
249 IN ULONG UserId,
250 IN ULONG GroupId,
251 IN ULONG GroupAttributes)
252 {
253 PSAM_DB_OBJECT UserObject = NULL;
254 PGROUP_MEMBERSHIP GroupsBuffer = NULL;
255 ULONG Length = 0;
256 ULONG i;
257 NTSTATUS Status;
258
259 Status = SampOpenUserObject(DomainObject,
260 UserId,
261 0,
262 &UserObject);
263 if (!NT_SUCCESS(Status))
264 {
265 return Status;
266 }
267
268 SampGetObjectAttribute(UserObject,
269 L"Groups",
270 NULL,
271 NULL,
272 &Length);
273
274 if (Length == 0)
275 return STATUS_UNSUCCESSFUL; /* FIXME */
276
277 GroupsBuffer = midl_user_allocate(Length);
278 if (GroupsBuffer == NULL)
279 {
280 Status = STATUS_INSUFFICIENT_RESOURCES;
281 goto done;
282 }
283
284 Status = SampGetObjectAttribute(UserObject,
285 L"Groups",
286 NULL,
287 GroupsBuffer,
288 &Length);
289 if (!NT_SUCCESS(Status))
290 goto done;
291
292 for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++)
293 {
294 if (GroupsBuffer[i].RelativeId == GroupId)
295 {
296 GroupsBuffer[i].Attributes = GroupAttributes;
297 break;
298 }
299 }
300
301 Status = SampSetObjectAttribute(UserObject,
302 L"Groups",
303 REG_BINARY,
304 GroupsBuffer,
305 Length);
306
307 done:
308 if (GroupsBuffer != NULL)
309 midl_user_free(GroupsBuffer);
310
311 if (UserObject != NULL)
312 SampCloseDbObject(UserObject);
313
314 return Status;
315 }
316
317
318 NTSTATUS
SampRemoveUserFromAllGroups(IN PSAM_DB_OBJECT UserObject)319 SampRemoveUserFromAllGroups(IN PSAM_DB_OBJECT UserObject)
320 {
321 PGROUP_MEMBERSHIP GroupsBuffer = NULL;
322 PSAM_DB_OBJECT GroupObject;
323 ULONG Length = 0;
324 ULONG i;
325 NTSTATUS Status;
326
327 SampGetObjectAttribute(UserObject,
328 L"Groups",
329 NULL,
330 NULL,
331 &Length);
332
333 if (Length == 0)
334 return STATUS_SUCCESS;
335
336 GroupsBuffer = midl_user_allocate(Length);
337 if (GroupsBuffer == NULL)
338 {
339 Status = STATUS_INSUFFICIENT_RESOURCES;
340 goto done;
341 }
342
343 Status = SampGetObjectAttribute(UserObject,
344 L"Groups",
345 NULL,
346 GroupsBuffer,
347 &Length);
348 if (!NT_SUCCESS(Status))
349 goto done;
350
351 for (i = 0; i < (Length / sizeof(GROUP_MEMBERSHIP)); i++)
352 {
353 Status = SampOpenGroupObject(UserObject->ParentObject,
354 GroupsBuffer[i].RelativeId,
355 0,
356 &GroupObject);
357 if (!NT_SUCCESS(Status))
358 {
359 goto done;
360 }
361
362 Status = SampRemoveMemberFromGroup(GroupObject,
363 UserObject->RelativeId);
364 if (Status == STATUS_MEMBER_NOT_IN_GROUP)
365 Status = STATUS_SUCCESS;
366
367 SampCloseDbObject(GroupObject);
368
369 if (!NT_SUCCESS(Status))
370 {
371 goto done;
372 }
373 }
374
375 /* Remove all groups from the Groups attribute */
376 Status = SampSetObjectAttribute(UserObject,
377 L"Groups",
378 REG_BINARY,
379 NULL,
380 0);
381
382 done:
383 if (GroupsBuffer != NULL)
384 midl_user_free(GroupsBuffer);
385
386 return Status;
387 }
388
389
390 NTSTATUS
SampRemoveUserFromAllAliases(IN PSAM_DB_OBJECT UserObject)391 SampRemoveUserFromAllAliases(IN PSAM_DB_OBJECT UserObject)
392 {
393 FIXME("(%p)\n", UserObject);
394 return STATUS_SUCCESS;
395 }
396
397
398 NTSTATUS
SampSetUserPassword(IN PSAM_DB_OBJECT UserObject,IN PENCRYPTED_NT_OWF_PASSWORD NtPassword,IN BOOLEAN NtPasswordPresent,IN PENCRYPTED_LM_OWF_PASSWORD LmPassword,IN BOOLEAN LmPasswordPresent)399 SampSetUserPassword(IN PSAM_DB_OBJECT UserObject,
400 IN PENCRYPTED_NT_OWF_PASSWORD NtPassword,
401 IN BOOLEAN NtPasswordPresent,
402 IN PENCRYPTED_LM_OWF_PASSWORD LmPassword,
403 IN BOOLEAN LmPasswordPresent)
404 {
405 PENCRYPTED_NT_OWF_PASSWORD NtHistory = NULL;
406 PENCRYPTED_LM_OWF_PASSWORD LmHistory = NULL;
407 ULONG NtHistoryLength = 0;
408 ULONG LmHistoryLength = 0;
409 ULONG CurrentHistoryLength;
410 ULONG MaxHistoryLength = 3;
411 ULONG Length = 0;
412 BOOLEAN UseNtPassword;
413 BOOLEAN UseLmPassword;
414 NTSTATUS Status;
415
416 UseNtPassword =
417 ((NtPasswordPresent != FALSE) &&
418 (NtPassword != NULL) &&
419 (memcmp(NtPassword, &EmptyNtHash, sizeof(ENCRYPTED_NT_OWF_PASSWORD)) != 0));
420
421 UseLmPassword =
422 ((LmPasswordPresent != FALSE) &&
423 (LmPassword != NULL) &&
424 (memcmp(LmPassword, &EmptyLmHash, sizeof(ENCRYPTED_LM_OWF_PASSWORD)) != 0));
425
426 /* Update the NT password history only if we have a new non-empty NT password */
427 if (UseNtPassword)
428 {
429 /* Get the size of the NT history */
430 SampGetObjectAttribute(UserObject,
431 L"NTPwdHistory",
432 NULL,
433 NULL,
434 &Length);
435
436 CurrentHistoryLength = Length / sizeof(ENCRYPTED_NT_OWF_PASSWORD);
437 if (CurrentHistoryLength < MaxHistoryLength)
438 {
439 NtHistoryLength = (CurrentHistoryLength + 1) * sizeof(ENCRYPTED_NT_OWF_PASSWORD);
440 }
441 else
442 {
443 NtHistoryLength = MaxHistoryLength * sizeof(ENCRYPTED_NT_OWF_PASSWORD);
444 }
445
446 /* Allocate the history buffer */
447 NtHistory = midl_user_allocate(NtHistoryLength);
448 if (NtHistory == NULL)
449 return STATUS_INSUFFICIENT_RESOURCES;
450
451 if (Length > 0)
452 {
453 /* Get the history */
454 Status = SampGetObjectAttribute(UserObject,
455 L"NTPwdHistory",
456 NULL,
457 NtHistory,
458 &Length);
459 if (!NT_SUCCESS(Status))
460 goto done;
461 }
462
463 /* Move the old passwords down by one entry */
464 if (NtHistoryLength > sizeof(ENCRYPTED_NT_OWF_PASSWORD))
465 {
466 MoveMemory(&(NtHistory[1]),
467 &(NtHistory[0]),
468 NtHistoryLength - sizeof(ENCRYPTED_NT_OWF_PASSWORD));
469 }
470
471 /* Add the new password to the top of the history */
472 if (NtPasswordPresent)
473 {
474 CopyMemory(&(NtHistory[0]),
475 NtPassword,
476 sizeof(ENCRYPTED_NT_OWF_PASSWORD));
477 }
478 else
479 {
480 ZeroMemory(&(NtHistory[0]),
481 sizeof(ENCRYPTED_NT_OWF_PASSWORD));
482 }
483
484 /* Set the history */
485 Status = SampSetObjectAttribute(UserObject,
486 L"NTPwdHistory",
487 REG_BINARY,
488 (PVOID)NtHistory,
489 NtHistoryLength);
490 if (!NT_SUCCESS(Status))
491 goto done;
492 }
493
494 /* Update the LM password history only if we have a new non-empty LM password */
495 if (UseLmPassword)
496 {
497 /* Get the size of the LM history */
498 Length = 0;
499 SampGetObjectAttribute(UserObject,
500 L"LMPwdHistory",
501 NULL,
502 NULL,
503 &Length);
504
505 CurrentHistoryLength = Length / sizeof(ENCRYPTED_LM_OWF_PASSWORD);
506 if (CurrentHistoryLength < MaxHistoryLength)
507 {
508 LmHistoryLength = (CurrentHistoryLength + 1) * sizeof(ENCRYPTED_LM_OWF_PASSWORD);
509 }
510 else
511 {
512 LmHistoryLength = MaxHistoryLength * sizeof(ENCRYPTED_LM_OWF_PASSWORD);
513 }
514
515 /* Allocate the history buffer */
516 LmHistory = midl_user_allocate(LmHistoryLength);
517 if (LmHistory == NULL)
518 return STATUS_INSUFFICIENT_RESOURCES;
519
520 if (Length > 0)
521 {
522 /* Get the history */
523 Status = SampGetObjectAttribute(UserObject,
524 L"LMPwdHistory",
525 NULL,
526 LmHistory,
527 &Length);
528 if (!NT_SUCCESS(Status))
529 goto done;
530 }
531
532 /* Move the old passwords down by one entry */
533 if (LmHistoryLength > sizeof(ENCRYPTED_LM_OWF_PASSWORD))
534 {
535 MoveMemory(&(LmHistory[1]),
536 &(LmHistory[0]),
537 LmHistoryLength - sizeof(ENCRYPTED_LM_OWF_PASSWORD));
538 }
539
540 /* Add the new password to the top of the history */
541 if (LmPasswordPresent)
542 {
543 CopyMemory(&(LmHistory[0]),
544 LmPassword,
545 sizeof(ENCRYPTED_LM_OWF_PASSWORD));
546 }
547 else
548 {
549 ZeroMemory(&(LmHistory[0]),
550 sizeof(ENCRYPTED_LM_OWF_PASSWORD));
551 }
552
553 /* Set the LM password history */
554 Status = SampSetObjectAttribute(UserObject,
555 L"LMPwdHistory",
556 REG_BINARY,
557 (PVOID)LmHistory,
558 LmHistoryLength);
559 if (!NT_SUCCESS(Status))
560 goto done;
561 }
562
563 /* Set the new NT password */
564 if (UseNtPassword)
565 {
566 Status = SampSetObjectAttribute(UserObject,
567 L"NTPwd",
568 REG_BINARY,
569 (PVOID)NtPassword,
570 sizeof(ENCRYPTED_NT_OWF_PASSWORD));
571 if (!NT_SUCCESS(Status))
572 goto done;
573 }
574 else
575 {
576 Status = SampSetObjectAttribute(UserObject,
577 L"NTPwd",
578 REG_BINARY,
579 &EmptyNtHash,
580 sizeof(ENCRYPTED_NT_OWF_PASSWORD));
581 if (!NT_SUCCESS(Status))
582 goto done;
583 }
584
585 /* Set the new LM password */
586 if (UseLmPassword)
587 {
588 Status = SampSetObjectAttribute(UserObject,
589 L"LMPwd",
590 REG_BINARY,
591 (PVOID)LmPassword,
592 sizeof(ENCRYPTED_LM_OWF_PASSWORD));
593 if (!NT_SUCCESS(Status))
594 goto done;
595 }
596 else
597 {
598 Status = SampSetObjectAttribute(UserObject,
599 L"LMPwd",
600 REG_BINARY,
601 &EmptyLmHash,
602 sizeof(ENCRYPTED_LM_OWF_PASSWORD));
603 if (!NT_SUCCESS(Status))
604 goto done;
605 }
606
607 done:
608 if (NtHistory != NULL)
609 midl_user_free(NtHistory);
610
611 if (LmHistory != NULL)
612 midl_user_free(LmHistory);
613
614 return Status;
615 }
616
617
618 NTSTATUS
SampGetLogonHoursAttribute(IN PSAM_DB_OBJECT UserObject,IN OUT PSAMPR_LOGON_HOURS LogonHours)619 SampGetLogonHoursAttribute(IN PSAM_DB_OBJECT UserObject,
620 IN OUT PSAMPR_LOGON_HOURS LogonHours)
621 {
622 PUCHAR RawBuffer = NULL;
623 ULONG Length = 0;
624 ULONG BufferLength = 0;
625 NTSTATUS Status;
626
627 Status = SampGetObjectAttribute(UserObject,
628 L"LogonHours",
629 NULL,
630 NULL,
631 &Length);
632 if (Status != STATUS_BUFFER_OVERFLOW)
633 {
634 TRACE("SampGetObjectAttribute failed (Status 0x%08lx)\n", Status);
635 return Status;
636 }
637
638 Status = STATUS_SUCCESS;
639
640 if (Length == 0)
641 {
642 LogonHours->UnitsPerWeek = 0;
643 LogonHours->LogonHours = NULL;
644 }
645 else
646 {
647 RawBuffer = midl_user_allocate(Length);
648 if (RawBuffer == NULL)
649 {
650 Status = STATUS_INSUFFICIENT_RESOURCES;
651 goto done;
652 }
653
654 Status = SampGetObjectAttribute(UserObject,
655 L"LogonHours",
656 NULL,
657 (PVOID)RawBuffer,
658 &Length);
659 if (!NT_SUCCESS(Status))
660 goto done;
661
662 LogonHours->UnitsPerWeek = *((PUSHORT)RawBuffer);
663
664 BufferLength = (((ULONG)LogonHours->UnitsPerWeek) + 7) / 8;
665
666 LogonHours->LogonHours = midl_user_allocate(BufferLength);
667 if (LogonHours->LogonHours == NULL)
668 {
669 TRACE("Failed to allocate LogonHours buffer!\n");
670 Status = STATUS_INSUFFICIENT_RESOURCES;
671 goto done;
672 }
673
674 memcpy(LogonHours->LogonHours,
675 &(RawBuffer[2]),
676 BufferLength);
677 }
678
679 done:
680
681 if (RawBuffer != NULL)
682 midl_user_free(RawBuffer);
683
684 return Status;
685 }
686
687
688 NTSTATUS
SampSetLogonHoursAttribute(IN PSAM_DB_OBJECT UserObject,IN PSAMPR_LOGON_HOURS LogonHours)689 SampSetLogonHoursAttribute(IN PSAM_DB_OBJECT UserObject,
690 IN PSAMPR_LOGON_HOURS LogonHours)
691 {
692 PUCHAR RawBuffer = NULL;
693 ULONG BufferLength;
694 ULONG Length = 0;
695 NTSTATUS Status;
696
697 if (LogonHours->UnitsPerWeek > 0)
698 {
699 BufferLength = (((ULONG)LogonHours->UnitsPerWeek) + 7) / 8;
700
701 Length = BufferLength + sizeof(USHORT);
702
703 RawBuffer = midl_user_allocate(Length);
704 if (RawBuffer == NULL)
705 {
706 Status = STATUS_INSUFFICIENT_RESOURCES;
707 goto done;
708 }
709
710 *((PUSHORT)RawBuffer) = LogonHours->UnitsPerWeek;
711
712 memcpy(&(RawBuffer[2]),
713 LogonHours->LogonHours,
714 BufferLength);
715 }
716
717 Status = SampSetObjectAttribute(UserObject,
718 L"LogonHours",
719 REG_BINARY,
720 RawBuffer,
721 Length);
722
723 done:
724 if (RawBuffer != NULL)
725 midl_user_free(RawBuffer);
726
727 return Status;
728 }
729
730 /* EOF */
731