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