[NET] Implement the undocumented /RANDOM option to generate random passwords
[reactos.git] / base / applications / network / net / cmdUser.c
1 /*
2 * COPYRIGHT: See COPYING in the top level directory
3 * PROJECT: ReactOS net command
4 * FILE: base/applications/network/net/cmdUser.c
5 * PURPOSE:
6 *
7 * PROGRAMMERS: Eric Kohl
8 * Curtis Wilson
9 */
10
11 #include "net.h"
12
13 static WCHAR szPasswordChars[] = L"0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ@#$%_-+:";
14
15 static
16 int
17 CompareUserInfo(const void *a, const void *b)
18 {
19 return _wcsicmp(((PUSER_INFO_0)a)->usri0_name,
20 ((PUSER_INFO_0)b)->usri0_name);
21 }
22
23
24 static
25 NET_API_STATUS
26 EnumerateUsers(VOID)
27 {
28 PUSER_INFO_0 pBuffer = NULL;
29 PSERVER_INFO_100 pServer = NULL;
30 DWORD dwRead = 0, dwTotal = 0;
31 DWORD i;
32 DWORD_PTR ResumeHandle = 0;
33 NET_API_STATUS Status;
34
35 Status = NetServerGetInfo(NULL,
36 100,
37 (LPBYTE*)&pServer);
38 if (Status != NERR_Success)
39 return Status;
40
41 ConPuts(StdOut, L"\n");
42 ConResPrintf(StdOut, IDS_USER_ACCOUNTS, pServer->sv100_name);
43 ConPuts(StdOut, L"\n\n");
44 PrintPadding(L'-', 79);
45 ConPuts(StdOut, L"\n");
46
47 NetApiBufferFree(pServer);
48
49 do
50 {
51 Status = NetUserEnum(NULL,
52 0,
53 0,
54 (LPBYTE*)&pBuffer,
55 MAX_PREFERRED_LENGTH,
56 &dwRead,
57 &dwTotal,
58 &ResumeHandle);
59 if ((Status != NERR_Success) && (Status != ERROR_MORE_DATA))
60 return Status;
61
62 qsort(pBuffer,
63 dwRead,
64 sizeof(PUSER_INFO_0),
65 CompareUserInfo);
66
67 for (i = 0; i < dwRead; i++)
68 {
69 if (pBuffer[i].usri0_name)
70 ConPrintf(StdOut, L"%s\n", pBuffer[i].usri0_name);
71 }
72
73 NetApiBufferFree(pBuffer);
74 pBuffer = NULL;
75 }
76 while (Status == ERROR_MORE_DATA);
77
78 return NERR_Success;
79 }
80
81
82 static
83 VOID
84 PrintDateTime(DWORD dwSeconds)
85 {
86 LARGE_INTEGER Time;
87 FILETIME FileTime;
88 SYSTEMTIME SystemTime;
89 WCHAR DateBuffer[80];
90 WCHAR TimeBuffer[80];
91
92 RtlSecondsSince1970ToTime(dwSeconds, &Time);
93 FileTime.dwLowDateTime = Time.u.LowPart;
94 FileTime.dwHighDateTime = Time.u.HighPart;
95 FileTimeToLocalFileTime(&FileTime, &FileTime);
96 FileTimeToSystemTime(&FileTime, &SystemTime);
97
98 GetDateFormatW(LOCALE_USER_DEFAULT,
99 DATE_SHORTDATE,
100 &SystemTime,
101 NULL,
102 DateBuffer,
103 80);
104
105 GetTimeFormatW(LOCALE_USER_DEFAULT,
106 TIME_NOSECONDS,
107 &SystemTime,
108 NULL,
109 TimeBuffer,
110 80);
111
112 ConPrintf(StdOut, L"%s %s", DateBuffer, TimeBuffer);
113 }
114
115
116 static
117 DWORD
118 GetTimeInSeconds(VOID)
119 {
120 LARGE_INTEGER Time;
121 FILETIME FileTime;
122 DWORD dwSeconds;
123
124 GetSystemTimeAsFileTime(&FileTime);
125 Time.u.LowPart = FileTime.dwLowDateTime;
126 Time.u.HighPart = FileTime.dwHighDateTime;
127 RtlTimeToSecondsSince1970(&Time, &dwSeconds);
128
129 return dwSeconds;
130 }
131
132
133 static
134 NET_API_STATUS
135 DisplayUser(LPWSTR lpUserName)
136 {
137 PUSER_MODALS_INFO_0 pUserModals = NULL;
138 PUSER_INFO_4 pUserInfo = NULL;
139 PLOCALGROUP_USERS_INFO_0 pLocalGroupInfo = NULL;
140 PGROUP_USERS_INFO_0 pGroupInfo = NULL;
141 DWORD dwLocalGroupRead, dwLocalGroupTotal;
142 DWORD dwGroupRead, dwGroupTotal;
143 DWORD dwLastSet;
144 DWORD i;
145 INT nPaddedLength = 29;
146 NET_API_STATUS Status;
147
148 /* Modify the user */
149 Status = NetUserGetInfo(NULL,
150 lpUserName,
151 4,
152 (LPBYTE*)&pUserInfo);
153 if (Status != NERR_Success)
154 return Status;
155
156 Status = NetUserModalsGet(NULL,
157 0,
158 (LPBYTE*)&pUserModals);
159 if (Status != NERR_Success)
160 goto done;
161
162 Status = NetUserGetLocalGroups(NULL,
163 lpUserName,
164 0,
165 0,
166 (LPBYTE*)&pLocalGroupInfo,
167 MAX_PREFERRED_LENGTH,
168 &dwLocalGroupRead,
169 &dwLocalGroupTotal);
170 if (Status != NERR_Success)
171 goto done;
172
173 Status = NetUserGetGroups(NULL,
174 lpUserName,
175 0,
176 (LPBYTE*)&pGroupInfo,
177 MAX_PREFERRED_LENGTH,
178 &dwGroupRead,
179 &dwGroupTotal);
180 if (Status != NERR_Success)
181 goto done;
182
183 PrintPaddedResourceString(IDS_USER_NAME, nPaddedLength);
184 ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_name);
185
186 PrintPaddedResourceString(IDS_USER_FULL_NAME, nPaddedLength);
187 ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_full_name);
188
189 PrintPaddedResourceString(IDS_USER_COMMENT, nPaddedLength);
190 ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_comment);
191
192 PrintPaddedResourceString(IDS_USER_USER_COMMENT, nPaddedLength);
193 ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_usr_comment);
194
195 PrintPaddedResourceString(IDS_USER_COUNTRY_CODE, nPaddedLength);
196 ConPrintf(StdOut, L"%03ld ()\n", pUserInfo->usri4_country_code);
197
198 PrintPaddedResourceString(IDS_USER_ACCOUNT_ACTIVE, nPaddedLength);
199 if (pUserInfo->usri4_flags & UF_ACCOUNTDISABLE)
200 ConResPuts(StdOut, IDS_GENERIC_NO);
201 else if (pUserInfo->usri4_flags & UF_LOCKOUT)
202 ConResPuts(StdOut, IDS_GENERIC_LOCKED);
203 else
204 ConResPuts(StdOut, IDS_GENERIC_YES);
205 ConPuts(StdOut, L"\n");
206
207 PrintPaddedResourceString(IDS_USER_ACCOUNT_EXPIRES, nPaddedLength);
208 if (pUserInfo->usri4_acct_expires == TIMEQ_FOREVER)
209 ConResPuts(StdOut, IDS_GENERIC_NEVER);
210 else
211 PrintDateTime(pUserInfo->usri4_acct_expires);
212 ConPuts(StdOut, L"\n\n");
213
214 PrintPaddedResourceString(IDS_USER_PW_LAST_SET, nPaddedLength);
215 dwLastSet = GetTimeInSeconds() - pUserInfo->usri4_password_age;
216 PrintDateTime(dwLastSet);
217 ConPuts(StdOut, L"\n");
218
219 PrintPaddedResourceString(IDS_USER_PW_EXPIRES, nPaddedLength);
220 if ((pUserInfo->usri4_flags & UF_DONT_EXPIRE_PASSWD) || pUserModals->usrmod0_max_passwd_age == TIMEQ_FOREVER)
221 ConResPuts(StdOut, IDS_GENERIC_NEVER);
222 else
223 PrintDateTime(dwLastSet + pUserModals->usrmod0_max_passwd_age);
224 ConPuts(StdOut, L"\n");
225
226 PrintPaddedResourceString(IDS_USER_PW_CHANGEABLE, nPaddedLength);
227 PrintDateTime(dwLastSet + pUserModals->usrmod0_min_passwd_age);
228 ConPuts(StdOut, L"\n");
229
230 PrintPaddedResourceString(IDS_USER_PW_REQUIRED, nPaddedLength);
231 ConResPuts(StdOut, (pUserInfo->usri4_flags & UF_PASSWD_NOTREQD) ? IDS_GENERIC_NO : IDS_GENERIC_YES);
232 ConPuts(StdOut, L"\n");
233
234 PrintPaddedResourceString(IDS_USER_CHANGE_PW, nPaddedLength);
235 ConResPuts(StdOut, (pUserInfo->usri4_flags & UF_PASSWD_CANT_CHANGE) ? IDS_GENERIC_NO : IDS_GENERIC_YES);
236 ConPuts(StdOut, L"\n\n");
237
238 PrintPaddedResourceString(IDS_USER_WORKSTATIONS, nPaddedLength);
239 if (pUserInfo->usri4_workstations == NULL || wcslen(pUserInfo->usri4_workstations) == 0)
240 ConResPuts(StdOut, IDS_GENERIC_ALL);
241 else
242 ConPrintf(StdOut, L"%s", pUserInfo->usri4_workstations);
243 ConPuts(StdOut, L"\n");
244
245 PrintPaddedResourceString(IDS_USER_LOGON_SCRIPT, nPaddedLength);
246 ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_script_path);
247
248 PrintPaddedResourceString(IDS_USER_PROFILE, nPaddedLength);
249 ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_profile);
250
251 PrintPaddedResourceString(IDS_USER_HOME_DIR, nPaddedLength);
252 ConPrintf(StdOut, L"%s\n", pUserInfo->usri4_home_dir);
253
254 PrintPaddedResourceString(IDS_USER_LAST_LOGON, nPaddedLength);
255 if (pUserInfo->usri4_last_logon == 0)
256 ConResPuts(StdOut, IDS_GENERIC_NEVER);
257 else
258 PrintDateTime(pUserInfo->usri4_last_logon);
259 ConPuts(StdOut, L"\n\n");
260
261 PrintPaddedResourceString(IDS_USER_LOGON_HOURS, nPaddedLength);
262 if (pUserInfo->usri4_logon_hours == NULL)
263 ConResPuts(StdOut, IDS_GENERIC_ALL);
264 ConPuts(StdOut, L"\n\n");
265
266 ConPuts(StdOut, L"\n");
267 PrintPaddedResourceString(IDS_USER_LOCAL_GROUPS, nPaddedLength);
268 if (dwLocalGroupTotal != 0 && pLocalGroupInfo != NULL)
269 {
270 for (i = 0; i < dwLocalGroupTotal; i++)
271 {
272 if (i != 0)
273 PrintPadding(L' ', nPaddedLength);
274 ConPrintf(StdOut, L"*%s\n", pLocalGroupInfo[i].lgrui0_name);
275 }
276 }
277 else
278 {
279 ConPuts(StdOut, L"\n");
280 }
281
282 PrintPaddedResourceString(IDS_USER_GLOBAL_GROUPS, nPaddedLength);
283 if (dwGroupTotal != 0 && pGroupInfo != NULL)
284 {
285 for (i = 0; i < dwGroupTotal; i++)
286 {
287 if (i != 0)
288 PrintPadding(L' ', nPaddedLength);
289 ConPrintf(StdOut, L"*%s\n", pGroupInfo[i].grui0_name);
290 }
291 }
292 else
293 {
294 ConPuts(StdOut, L"\n");
295 }
296
297 done:
298 if (pGroupInfo != NULL)
299 NetApiBufferFree(pGroupInfo);
300
301 if (pLocalGroupInfo != NULL)
302 NetApiBufferFree(pLocalGroupInfo);
303
304 if (pUserModals != NULL)
305 NetApiBufferFree(pUserModals);
306
307 if (pUserInfo != NULL)
308 NetApiBufferFree(pUserInfo);
309
310 return NERR_Success;
311 }
312
313
314 static
315 VOID
316 ReadPassword(
317 LPWSTR *lpPassword,
318 LPBOOL lpAllocated)
319 {
320 WCHAR szPassword1[PWLEN + 1];
321 WCHAR szPassword2[PWLEN + 1];
322 LPWSTR ptr;
323
324 *lpAllocated = FALSE;
325
326 while (TRUE)
327 {
328 ConResPuts(StdOut, IDS_USER_ENTER_PASSWORD1);
329 ReadFromConsole(szPassword1, PWLEN + 1, FALSE);
330 ConPuts(StdOut, L"\n");
331
332 ConResPuts(StdOut, IDS_USER_ENTER_PASSWORD2);
333 ReadFromConsole(szPassword2, PWLEN + 1, FALSE);
334 ConPuts(StdOut, L"\n");
335
336 if (wcslen(szPassword1) == wcslen(szPassword2) &&
337 wcscmp(szPassword1, szPassword2) == 0)
338 {
339 ptr = HeapAlloc(GetProcessHeap(),
340 0,
341 (wcslen(szPassword1) + 1) * sizeof(WCHAR));
342 if (ptr != NULL)
343 {
344 wcscpy(ptr, szPassword1);
345 *lpPassword = ptr;
346 *lpAllocated = TRUE;
347 return;
348 }
349 }
350 else
351 {
352 ConPuts(StdOut, L"\n");
353 ConResPuts(StdOut, IDS_USER_NO_PASSWORD_MATCH);
354 ConPuts(StdOut, L"\n");
355 *lpPassword = NULL;
356 }
357 }
358 }
359
360
361 static
362 VOID
363 GenerateRandomPassword(
364 LPWSTR *lpPassword,
365 LPBOOL lpAllocated)
366 {
367 LPWSTR pPassword = NULL;
368 INT nCharsLen, i, nLength = 8;
369
370 srand(GetTickCount());
371
372 pPassword = HeapAlloc(GetProcessHeap(),
373 HEAP_ZERO_MEMORY,
374 (nLength + 1) * sizeof(WCHAR));
375 if (pPassword == NULL)
376 return;
377
378 nCharsLen = wcslen(szPasswordChars);
379
380 for (i = 0; i < nLength; i++)
381 {
382 pPassword[i] = szPasswordChars[rand() % nCharsLen];
383 }
384
385 *lpPassword = pPassword;
386 *lpAllocated = TRUE;
387 }
388
389
390 INT
391 cmdUser(
392 INT argc,
393 WCHAR **argv)
394 {
395 INT i, j;
396 INT result = 0;
397 BOOL bAdd = FALSE;
398 BOOL bDelete = FALSE;
399 #if 0
400 BOOL bDomain = FALSE;
401 #endif
402 BOOL bRandomPassword = FALSE;
403 LPWSTR lpUserName = NULL;
404 LPWSTR lpPassword = NULL;
405 PUSER_INFO_4 pUserInfo = NULL;
406 USER_INFO_4 UserInfo;
407 LPWSTR p;
408 LPWSTR endptr;
409 DWORD value;
410 BOOL bPasswordAllocated = FALSE;
411 NET_API_STATUS Status;
412
413 if (argc == 2)
414 {
415 Status = EnumerateUsers();
416 ConPrintf(StdOut, L"Status: %lu\n", Status);
417 return 0;
418 }
419 else if (argc == 3)
420 {
421 Status = DisplayUser(argv[2]);
422 ConPrintf(StdOut, L"Status: %lu\n", Status);
423 return 0;
424 }
425
426 i = 2;
427 if (argv[i][0] != L'/')
428 {
429 lpUserName = argv[i];
430 // ConPrintf(StdOut, L"User: %s\n", lpUserName);
431 i++;
432 }
433
434 if (argv[i][0] != L'/')
435 {
436 lpPassword = argv[i];
437 // ConPrintf(StdOut, L"Password: %s\n", lpPassword);
438 i++;
439 }
440
441 for (j = i; j < argc; j++)
442 {
443 if (_wcsicmp(argv[j], L"/help") == 0)
444 {
445 ConResPuts(StdOut, IDS_USER_HELP);
446 return 0;
447 }
448 else if (_wcsicmp(argv[j], L"/add") == 0)
449 {
450 bAdd = TRUE;
451 }
452 else if (_wcsicmp(argv[j], L"/delete") == 0)
453 {
454 bDelete = TRUE;
455 }
456 else if (_wcsicmp(argv[j], L"/domain") == 0)
457 {
458 ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/DOMAIN");
459 #if 0
460 bDomain = TRUE;
461 #endif
462 }
463 else if (_wcsicmp(argv[j], L"/random") == 0)
464 {
465 bRandomPassword = TRUE;
466 GenerateRandomPassword(&lpPassword,
467 &bPasswordAllocated);
468 }
469 }
470
471 if (bAdd && bDelete)
472 {
473 result = 1;
474 goto done;
475 }
476
477 /* Interactive password input */
478 if (lpPassword != NULL && wcscmp(lpPassword, L"*") == 0)
479 {
480 ReadPassword(&lpPassword,
481 &bPasswordAllocated);
482 }
483
484 if (!bAdd && !bDelete)
485 {
486 /* Modify the user */
487 Status = NetUserGetInfo(NULL,
488 lpUserName,
489 4,
490 (LPBYTE*)&pUserInfo);
491 if (Status != NERR_Success)
492 {
493 ConPrintf(StdOut, L"Status: %lu\n", Status);
494 result = 1;
495 goto done;
496 }
497 }
498 else if (bAdd && !bDelete)
499 {
500 /* Add the user */
501 ZeroMemory(&UserInfo, sizeof(USER_INFO_4));
502
503 UserInfo.usri4_name = lpUserName;
504 UserInfo.usri4_password = lpPassword;
505 UserInfo.usri4_flags = UF_SCRIPT | UF_NORMAL_ACCOUNT;
506
507 pUserInfo = &UserInfo;
508 }
509
510 for (j = i; j < argc; j++)
511 {
512 if (_wcsnicmp(argv[j], L"/active:", 8) == 0)
513 {
514 p = &argv[i][8];
515 if (_wcsicmp(p, L"yes") == 0)
516 {
517 pUserInfo->usri4_flags &= ~UF_ACCOUNTDISABLE;
518 }
519 else if (_wcsicmp(p, L"no") == 0)
520 {
521 pUserInfo->usri4_flags |= UF_ACCOUNTDISABLE;
522 }
523 else
524 {
525 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/ACTIVE");
526 result = 1;
527 goto done;
528 }
529 }
530 else if (_wcsnicmp(argv[j], L"/comment:", 9) == 0)
531 {
532 pUserInfo->usri4_comment = &argv[j][9];
533 }
534 else if (_wcsnicmp(argv[j], L"/countrycode:", 13) == 0)
535 {
536 p = &argv[i][13];
537 value = wcstoul(p, &endptr, 10);
538 if (*endptr != 0)
539 {
540 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/COUNTRYCODE");
541 result = 1;
542 goto done;
543 }
544
545 /* FIXME: verify the country code */
546
547 pUserInfo->usri4_country_code = value;
548 }
549 else if (_wcsnicmp(argv[j], L"/expires:", 9) == 0)
550 {
551 p = &argv[i][9];
552 if (_wcsicmp(p, L"never") == 0)
553 {
554 pUserInfo->usri4_acct_expires = TIMEQ_FOREVER;
555 }
556 else
557 {
558 /* FIXME: Parse the date */
559 ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/EXPIRES");
560 }
561 }
562 else if (_wcsnicmp(argv[j], L"/fullname:", 10) == 0)
563 {
564 pUserInfo->usri4_full_name = &argv[j][10];
565 }
566 else if (_wcsnicmp(argv[j], L"/homedir:", 9) == 0)
567 {
568 pUserInfo->usri4_home_dir = &argv[j][9];
569 }
570 else if (_wcsnicmp(argv[j], L"/passwordchg:", 13) == 0)
571 {
572 p = &argv[i][13];
573 if (_wcsicmp(p, L"yes") == 0)
574 {
575 pUserInfo->usri4_flags &= ~UF_PASSWD_CANT_CHANGE;
576 }
577 else if (_wcsicmp(p, L"no") == 0)
578 {
579 pUserInfo->usri4_flags |= UF_PASSWD_CANT_CHANGE;
580 }
581 else
582 {
583 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/PASSWORDCHG");
584 result = 1;
585 goto done;
586 }
587 }
588 else if (_wcsnicmp(argv[j], L"/passwordreq:", 13) == 0)
589 {
590 p = &argv[i][13];
591 if (_wcsicmp(p, L"yes") == 0)
592 {
593 pUserInfo->usri4_flags &= ~UF_PASSWD_NOTREQD;
594 }
595 else if (_wcsicmp(p, L"no") == 0)
596 {
597 pUserInfo->usri4_flags |= UF_PASSWD_NOTREQD;
598 }
599 else
600 {
601 ConResPrintf(StdErr, IDS_ERROR_INVALID_OPTION_VALUE, L"/PASSWORDREQ");
602 result = 1;
603 goto done;
604 }
605 }
606 else if (_wcsnicmp(argv[j], L"/profilepath:", 13) == 0)
607 {
608 pUserInfo->usri4_profile = &argv[j][13];
609 }
610 else if (_wcsnicmp(argv[j], L"/scriptpath:", 12) == 0)
611 {
612 pUserInfo->usri4_script_path = &argv[j][12];
613 }
614 else if (_wcsnicmp(argv[j], L"/times:", 7) == 0)
615 {
616 /* FIXME */
617 ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/TIMES");
618 }
619 else if (_wcsnicmp(argv[j], L"/usercomment:", 13) == 0)
620 {
621 pUserInfo->usri4_usr_comment = &argv[j][13];
622 }
623 else if (_wcsnicmp(argv[j], L"/workstations:", 14) == 0)
624 {
625 /* FIXME */
626 ConResPrintf(StdErr, IDS_ERROR_OPTION_NOT_SUPPORTED, L"/WORKSTATIONS");
627 }
628 }
629
630 if (!bAdd && !bDelete)
631 {
632 /* Modify the user */
633 Status = NetUserSetInfo(NULL,
634 lpUserName,
635 4,
636 (LPBYTE)pUserInfo,
637 NULL);
638 ConPrintf(StdOut, L"Status: %lu\n", Status);
639 }
640 else if (bAdd && !bDelete)
641 {
642 /* Add the user */
643 Status = NetUserAdd(NULL,
644 4,
645 (LPBYTE)pUserInfo,
646 NULL);
647 ConPrintf(StdOut, L"Status: %lu\n", Status);
648 }
649 else if (!bAdd && bDelete)
650 {
651 /* Delete the user */
652 Status = NetUserDel(NULL,
653 lpUserName);
654 ConPrintf(StdOut, L"Status: %lu\n", Status);
655 }
656
657 if (Status == NERR_Success &&
658 lpPassword != NULL &&
659 bRandomPassword == TRUE)
660 {
661 ConPrintf(StdOut, L"The password for %s is: %s\n", lpUserName, lpPassword);
662 }
663
664 done:
665 if ((bPasswordAllocated != FALSE) && (lpPassword != NULL))
666 HeapFree(GetProcessHeap(), 0, lpPassword);
667
668 if (!bAdd && !bDelete && pUserInfo != NULL)
669 NetApiBufferFree(pUserInfo);
670
671 if (result != 0)
672 {
673 ConResPuts(StdOut, IDS_GENERIC_SYNTAX);
674 ConResPuts(StdOut, IDS_USER_SYNTAX);
675 }
676
677 return result;
678 }
679
680 /* EOF */