OSDN Git Service

36c462267374b1984ffc6c1a641755c77e48ac81
[pf3gnuchains/pf3gnuchains4x.git] / winsup / utils / mkpasswd.c
1 /* mkpasswd.c:
2
3    Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006,
4    2008, 2009 Red Hat, Inc.
5
6    This file is part of Cygwin.
7
8    This software is a copyrighted work licensed under the terms of the
9    Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
10    details. */
11
12 #define _WIN32_WINNT 0x0600
13 #include <ctype.h>
14 #include <stdlib.h>
15 #include <wchar.h>
16 #include <wctype.h>
17 #include <locale.h>
18 #include <stdio.h>
19 #include <unistd.h>
20 #include <getopt.h>
21 #include <io.h>
22 #include <sys/fcntl.h>
23 #include <sys/cygwin.h>
24 #include <windows.h>
25 #include <lm.h>
26 #include <iptypes.h>
27 #include <wininet.h>
28 #include <ntsecapi.h>
29 #include <dsgetdc.h>
30 #include <ntdef.h>
31
32 #define print_win_error(x) _print_win_error(x, __LINE__)
33
34 #define MAX_SID_LEN 40
35
36 static const char version[] = "$Revision$";
37
38 extern char *__progname __declspec(dllimport);
39
40 SID_IDENTIFIER_AUTHORITY sid_world_auth = {SECURITY_WORLD_SID_AUTHORITY};
41 SID_IDENTIFIER_AUTHORITY sid_nt_auth = {SECURITY_NT_AUTHORITY};
42
43 NET_API_STATUS WINAPI (*dsgetdcname)(LPWSTR,LPWSTR,GUID*,LPWSTR,ULONG,PDOMAIN_CONTROLLER_INFOW*);
44
45 #ifndef min
46 #define min(a,b) (((a)<(b))?(a):(b))
47 #endif
48
49 typedef struct
50 {
51   char *str;
52   DWORD id_offset;
53   BOOL domain;
54   BOOL with_dom;
55 } domlist_t;
56
57 static void
58 _print_win_error(DWORD code, int line)
59 {
60   char buf[4096];
61
62   if (FormatMessage (FORMAT_MESSAGE_FROM_SYSTEM
63       | FORMAT_MESSAGE_IGNORE_INSERTS,
64       NULL,
65       code,
66       MAKELANGID (LANG_NEUTRAL, SUBLANG_DEFAULT),
67       (LPTSTR) buf, sizeof (buf), NULL))
68     fprintf (stderr, "mkpasswd (%d): [%lu] %s", line, code, buf);
69   else
70     fprintf (stderr, "mkpasswd (%d): error %lu", line, code);
71 }
72
73 static void
74 load_dsgetdcname ()
75 {
76   HANDLE h = LoadLibrary ("netapi32.dll");
77
78   if (h)
79     dsgetdcname = (void *) GetProcAddress (h, "DsGetDcNameW");
80 }
81
82 static PWCHAR
83 get_dcname (char *domain)
84 {
85   static WCHAR server[INTERNET_MAX_HOST_NAME_LENGTH + 1];
86   DWORD rc;
87   PWCHAR servername;
88   WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1];
89   PDOMAIN_CONTROLLER_INFOW pdci = NULL;
90
91   if (dsgetdcname)
92     {
93       if (domain)
94         {
95           mbstowcs (domain_name, domain, strlen (domain) + 1);
96           rc = dsgetdcname (NULL, domain_name, NULL, NULL, 0, &pdci);
97         }
98       else
99         rc = dsgetdcname (NULL, NULL, NULL, NULL, 0, &pdci);
100       if (rc != ERROR_SUCCESS)
101         {
102           print_win_error(rc);
103           return (PWCHAR) -1;
104         }
105       wcscpy (server, pdci->DomainControllerName);
106       NetApiBufferFree (pdci);
107     }
108   else
109     {
110       rc = NetGetDCName (NULL, NULL, (void *) &servername);
111       if (rc == ERROR_SUCCESS && domain)
112         {
113           LPWSTR server = servername;
114           mbstowcs (domain_name, domain, strlen (domain) + 1);
115           rc = NetGetDCName (server, domain_name, (void *) &servername);
116           NetApiBufferFree (server);
117         }
118       if (rc != ERROR_SUCCESS)
119         {
120           print_win_error(rc);
121           return (PWCHAR) -1;
122         }
123       wcscpy (server, servername);
124       NetApiBufferFree ((PVOID) servername);
125     }
126   return server;
127 }
128
129 static char *
130 put_sid (PSID sid)
131 {
132   static char s[512];
133   char t[32];
134   DWORD i;
135
136   strcpy (s, "S-1-");
137   sprintf(t, "%u", GetSidIdentifierAuthority (sid)->Value[5]);
138   strcat (s, t);
139   for (i = 0; i < *GetSidSubAuthorityCount (sid); ++i)
140     {
141       sprintf(t, "-%lu", *GetSidSubAuthority (sid, i));
142       strcat (s, t);
143     }
144   return s;
145 }
146
147 static void
148 psx_dir (char *in, char *out)
149 {
150   if (isalpha ((unsigned char) in[0]) && in[1] == ':')
151     {
152       sprintf (out, "/cygdrive/%c", in[0]);
153       in += 2;
154       out += strlen (out);
155     }
156
157   while (*in)
158     {
159       if (*in == '\\')
160         *out = '/';
161       else
162         *out = *in;
163       in++;
164       out++;
165     }
166
167   *out = '\0';
168 }
169
170 static void
171 uni2ansi (LPWSTR wcs, char *mbs, int size)
172 {
173   if (wcs)
174     wcstombs (mbs, wcs, size);
175   else
176     *mbs = '\0';
177 }
178
179 typedef struct {
180   PSID psid;
181   int buffer[10];
182 } sidbuf;
183
184 static sidbuf curr_user;
185 static sidbuf curr_pgrp;
186 static BOOL got_curr_user = FALSE;
187
188 static void
189 fetch_current_user_sid ()
190 {
191   DWORD len;
192   HANDLE ptok;
193
194   if (!OpenProcessToken (GetCurrentProcess (), TOKEN_QUERY, &ptok)
195       || !GetTokenInformation (ptok, TokenUser, &curr_user, sizeof curr_user,
196                                &len)
197       || !GetTokenInformation (ptok, TokenPrimaryGroup, &curr_pgrp,
198                                sizeof curr_pgrp, &len)
199       || !CloseHandle (ptok))
200     {
201       print_win_error (GetLastError ());
202       return;
203     }
204 }
205
206 static void
207 current_user (int print_cygpath, const char *sep, const char *passed_home_path,
208               DWORD id_offset, const char *disp_username)
209 {
210   WCHAR user[UNLEN + 1];
211   WCHAR dom[MAX_DOMAIN_NAME_LEN + 1];
212   DWORD ulen = UNLEN + 1;
213   DWORD dlen = MAX_DOMAIN_NAME_LEN + 1;
214   SID_NAME_USE acc_type;
215   int uid, gid;
216   char homedir_psx[PATH_MAX] = {0};
217
218   if (!curr_user.psid || !curr_pgrp.psid
219       || !LookupAccountSidW (NULL, curr_user.psid, user, &ulen, dom, &dlen,
220                              &acc_type))
221     {
222       print_win_error (GetLastError ());
223       return;
224     }
225
226   uid = *GetSidSubAuthority (curr_user.psid,
227                              *GetSidSubAuthorityCount(curr_user.psid) - 1);
228   gid = *GetSidSubAuthority (curr_pgrp.psid,
229                              *GetSidSubAuthorityCount(curr_pgrp.psid) - 1);
230   if (passed_home_path[0] == '\0')
231     {
232       char *envhome = getenv ("HOME");
233
234       if (envhome && envhome[0])
235         {
236           if (print_cygpath)
237             cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE, envhome,
238                               homedir_psx, PATH_MAX);
239           else
240             psx_dir (envhome, homedir_psx);
241         }
242       else
243         {
244           wcstombs (stpncpy (homedir_psx, "/home/", sizeof (homedir_psx)),
245                     user, sizeof (homedir_psx) - 6);
246           homedir_psx[PATH_MAX - 1] = '\0';
247         }
248     }
249   else
250     {
251       char *p = stpncpy (homedir_psx, passed_home_path, sizeof (homedir_psx));
252       wcstombs (p, user, sizeof (homedir_psx) - (p - homedir_psx));
253       homedir_psx[PATH_MAX - 1] = '\0';
254     }
255
256   printf ("%ls%s%ls:unused:%lu:%lu:U-%ls\\%ls,%s:%s:/bin/bash\n",
257           sep ? dom : L"",
258           sep ?: "",
259           user,
260           id_offset + uid,
261           id_offset + gid,
262           dom,
263           user,
264           put_sid (curr_user.psid),
265           homedir_psx);
266 }
267
268 static void
269 enum_unix_users (domlist_t *dom_or_machine, const char *sep, DWORD id_offset,
270                  char *unix_user_list)
271 {
272   WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1];
273   PWCHAR servername = NULL;
274   char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL;
275   BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE;
276   SID_IDENTIFIER_AUTHORITY auth = { { 0, 0, 0, 0, 0, 22 } };
277   char *ustr, *user_list;
278   WCHAR user[UNLEN + sizeof ("Unix User\\") + 1];
279   WCHAR dom[MAX_DOMAIN_NAME_LEN + 1];
280   DWORD ulen, dlen, sidlen;
281   PSID psid;
282   char psid_buffer[MAX_SID_LEN];
283   SID_NAME_USE acc_type;
284
285   if (!d_or_m)
286     return;
287
288   int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1);
289   if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1)
290     {
291       fprintf (stderr, "%s: Invalid machine name '%s'.  Skipping...\n",
292                __progname, d_or_m);
293       return;
294     }
295   servername = machine;
296
297   if (!AllocateAndInitializeSid (&auth, 2, 1, 0, 0, 0, 0, 0, 0, 0, &psid))
298     return;
299
300   if (!(user_list = strdup (unix_user_list)))
301     {
302       FreeSid (psid);
303       return;
304     }
305
306   for (ustr = strtok (user_list, ","); ustr; ustr = strtok (NULL, ","))
307     {
308       if (!isdigit ((unsigned char) ustr[0]) && ustr[0] != '-')
309         {
310           PWCHAR p = wcpcpy (user, L"Unix User\\");
311           ret = mbstowcs (p, ustr, UNLEN + 1);
312           if (ret < 1 || ret >= UNLEN + 1)
313             fprintf (stderr, "%s: Invalid user name '%s'.  Skipping...\n",
314                      __progname, ustr);
315           else if (LookupAccountNameW (servername, user,
316                                        psid = (PSID) psid_buffer,
317                                        (sidlen = MAX_SID_LEN, &sidlen),
318                                        dom,
319                                        (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen),
320                                        &acc_type))
321             printf ("%s%s%ls:unused:%lu:99999:,%s::\n",
322                     with_dom ? "Unix User" : "",
323                     with_dom ? sep : "",
324                     user + 10,
325                     id_offset +
326                     *GetSidSubAuthority (psid,
327                                          *GetSidSubAuthorityCount(psid) - 1),
328                     put_sid (psid));
329         }
330       else
331         {
332           DWORD start, stop;
333           char *p = ustr;
334           if (*p == '-')
335             start = 0;
336           else
337             start = strtol (p, &p, 10);
338           if (!*p)
339             stop = start;
340           else if (*p++ != '-' || !isdigit ((unsigned char) *p)
341                    || (stop = strtol (p, &p, 10)) < start || *p)
342             {
343               fprintf (stderr, "%s: Malformed unix user list entry '%s'.  "
344                                "Skipping...\n", __progname, ustr);
345               continue;
346             }
347           for (; start <= stop; ++ start)
348             {
349               *GetSidSubAuthority (psid, *GetSidSubAuthorityCount(psid) - 1)
350               = start;
351               if (LookupAccountSidW (servername, psid,
352                                      user, (ulen = GNLEN + 1, &ulen),
353                                      dom,
354                                      (dlen = MAX_DOMAIN_NAME_LEN + 1, &dlen),
355                                      &acc_type)
356                   && !iswdigit (user[0]))
357                 printf ("%s%s%ls:unused:%lu:99999:,%s::\n",
358                         with_dom ? "Unix User" : "",
359                         with_dom ? sep : "",
360                         user,
361                         id_offset + start,
362                         put_sid (psid));
363             }
364         }
365     }
366
367   free (user_list);
368   FreeSid (psid);
369 }
370
371 static int
372 enum_users (BOOL domain, domlist_t *dom_or_machine, const char *sep,
373             int print_cygpath, const char *passed_home_path, DWORD id_offset,
374             char *disp_username, int print_current)
375 {
376   WCHAR machine[INTERNET_MAX_HOST_NAME_LENGTH + 1];
377   PWCHAR servername = NULL;
378   char *d_or_m = dom_or_machine ? dom_or_machine->str : NULL;
379   BOOL with_dom = dom_or_machine ? dom_or_machine->with_dom : FALSE;
380   USER_INFO_3 *buffer;
381   DWORD entriesread = 0;
382   DWORD totalentries = 0;
383   DWORD resume_handle = 0;
384   DWORD rc;
385   WCHAR uni_name[UNLEN + 1];
386   if (domain)
387     {
388       servername = get_dcname (d_or_m);
389       if (servername == (PWCHAR) -1)
390         return 1;
391     }
392   else if (d_or_m)
393     {
394       int ret = mbstowcs (machine, d_or_m, INTERNET_MAX_HOST_NAME_LENGTH + 1);
395       if (ret < 1 || ret >= INTERNET_MAX_HOST_NAME_LENGTH + 1)
396         {
397           fprintf (stderr, "%s: Invalid machine name '%s'.  Skipping...\n",
398                    __progname, d_or_m);
399           return 1;
400         }
401       servername = machine;
402     }
403
404   do
405     {
406       DWORD i;
407
408       if (disp_username != NULL)
409         {
410           mbstowcs (uni_name, disp_username, UNLEN + 1);
411           rc = NetUserGetInfo (servername, (LPWSTR) &uni_name, 3,
412                                (void *) &buffer);
413           entriesread = 1;
414           /* Avoid annoying error messages just because the user hasn't been
415              found. */
416           if (rc == NERR_UserNotFound)
417             return 0;
418         }
419       else
420         rc = NetUserEnum (servername, 3, FILTER_NORMAL_ACCOUNT,
421                           (void *) &buffer, MAX_PREFERRED_LENGTH,
422                           &entriesread, &totalentries, &resume_handle);
423       switch (rc)
424         {
425         case ERROR_ACCESS_DENIED:
426           print_win_error(rc);
427           return 1;
428
429         case ERROR_MORE_DATA:
430         case ERROR_SUCCESS:
431           break;
432
433         default:
434           print_win_error(rc);
435           return 1;
436         }
437
438       for (i = 0; i < entriesread; i++)
439         {
440           char homedir_psx[PATH_MAX];
441           char homedir_w32[MAX_PATH];
442           WCHAR domain_name[MAX_DOMAIN_NAME_LEN + 1];
443           DWORD domname_len = MAX_DOMAIN_NAME_LEN + 1;
444           char psid_buffer[MAX_SID_LEN];
445           PSID psid = (PSID) psid_buffer;
446           DWORD sid_length = MAX_SID_LEN;
447           SID_NAME_USE acc_type;
448
449           int uid = buffer[i].usri3_user_id;
450           int gid = buffer[i].usri3_primary_group_id;
451           homedir_w32[0] = homedir_psx[0] = '\0';
452           if (passed_home_path[0] == '\0')
453             {
454               uni2ansi (buffer[i].usri3_home_dir, homedir_w32,
455                         sizeof (homedir_w32));
456               if (homedir_w32[0] != '\0')
457                 {
458                   if (print_cygpath)
459                     cygwin_conv_path (CCP_WIN_A_TO_POSIX | CCP_ABSOLUTE,
460                                       homedir_w32, homedir_psx, PATH_MAX);
461                   else
462                     psx_dir (homedir_w32, homedir_psx);
463                 }
464               else
465                 uni2ansi (buffer[i].usri3_name,
466                           stpcpy (homedir_psx, "/home/"), PATH_MAX - 6);
467             }
468           else
469             uni2ansi (buffer[i].usri3_name,
470                       stpcpy (homedir_psx, passed_home_path),
471                       PATH_MAX - strlen (passed_home_path));
472
473           if (!LookupAccountNameW (servername, buffer[i].usri3_name,
474                                    psid, &sid_length, domain_name,
475                                    &domname_len, &acc_type))
476             {
477               print_win_error(GetLastError ());
478               fprintf(stderr, " (%ls)\n", buffer[i].usri3_name);
479               continue;
480             }
481           else if (acc_type == SidTypeDomain)
482             {
483               WCHAR domname[MAX_DOMAIN_NAME_LEN + UNLEN + 2];
484
485               wcscpy (domname, domain || !servername
486                                ? domain_name : servername);
487               wcscat (domname, L"\\");
488               wcscat (domname, buffer[i].usri3_name);
489               sid_length = MAX_SID_LEN;
490               domname_len = sizeof (domname);
491               if (!LookupAccountNameW (servername, domname, psid,
492                                        &sid_length, domain_name,
493                                        &domname_len, &acc_type))
494                 {
495                   print_win_error(GetLastError ());
496                   fprintf(stderr, " (%ls)\n", domname);
497                   continue;
498                 }
499             }
500           if (!print_current)
501             /* fall through */;
502           else if (EqualSid (curr_user.psid, psid))
503             got_curr_user = TRUE;
504
505           printf ("%ls%s%ls:unused:%lu:%lu:%ls%sU-%ls\\%ls,%s:%s:/bin/bash\n",
506                   with_dom ? domain_name : L"",
507                   with_dom ? sep : "",
508                   buffer[i].usri3_name,
509                   id_offset + uid,
510                   id_offset + gid,
511                   buffer[i].usri3_full_name ?: L"",
512                   buffer[i].usri3_full_name
513                   && buffer[i].usri3_full_name[0] ? "," : "",
514                   domain_name,
515                   buffer[i].usri3_name,
516                   put_sid (psid),
517                   homedir_psx);
518         }
519
520       NetApiBufferFree (buffer);
521
522     }
523   while (rc == ERROR_MORE_DATA);
524
525   return 0;
526 }
527
528 static void
529 print_special (PSID_IDENTIFIER_AUTHORITY auth, BYTE cnt,
530                DWORD sub1, DWORD sub2, DWORD sub3, DWORD sub4,
531                DWORD sub5, DWORD sub6, DWORD sub7, DWORD sub8)
532 {
533   WCHAR user[UNLEN + 1], dom[MAX_DOMAIN_NAME_LEN + 1];
534   DWORD len, len2, rid;
535   PSID sid;
536   SID_NAME_USE acc_type;
537
538   if (AllocateAndInitializeSid (auth, cnt, sub1, sub2, sub3, sub4,
539                                 sub5, sub6, sub7, sub8, &sid))
540     {
541       if (LookupAccountSidW (NULL, sid,
542                              user, (len = UNLEN + 1, &len),
543                              dom, (len2 = MAX_DOMAIN_NAME_LEN + 1, &len),
544                              &acc_type))
545         {
546           if (sub8)
547             rid = sub8;
548           else if (sub7)
549             rid = sub7;
550           else if (sub6)
551             rid = sub6;
552           else if (sub5)
553             rid = sub5;
554           else if (sub4)
555             rid = sub4;
556           else if (sub3)
557             rid = sub3;
558           else if (sub2)
559             rid = sub2;
560           else
561             rid = sub1;
562           printf ("%ls:*:%lu:%lu:,%s::\n",
563                   user, rid, rid == 18 ? 544 : rid, /* SYSTEM hack */
564                   put_sid (sid));
565         }
566       FreeSid (sid);
567     }
568 }
569
570 static int
571 usage (FILE * stream)
572 {
573   fprintf (stream,
574 "Usage: mkpasswd [OPTIONS]...\n"
575 "Print /etc/passwd file to stdout\n"
576 "\n"
577 "Options:\n"
578 "   -l,--local [machine[,offset]]\n"
579 "                           print local user accounts with uid offset offset\n"
580 "                           (from local machine if no machine specified)\n"
581 "   -L,--Local [machine[,offset]]\n"
582 "                           ditto, but generate username with machine prefix\n"
583 "   -d,--domain [domain[,offset]]\n"
584 "                           print domain accounts with uid offset offset\n"
585 "                           (from current domain if no domain specified)\n"
586 "   -D,--Domain [domain[,offset]]\n"
587 "                           ditto, but generate username with domain prefix\n"
588 "   -c,--current            print current user\n"
589 "   -C,--Current            ditto, but generate username with machine or\n"
590 "                           domain prefix\n"
591 "   -S,--separator char     for -L, -D, -C use character char as domain\\user\n"
592 "                           separator in username instead of the default '\\'\n"
593 "   -o,--id-offset offset   change the default offset (10000) added to uids\n"
594 "                           in domain or foreign server accounts.\n"
595 "   -u,--username username  only return information for the specified user\n"
596 "                           one of -l, -L, -d, -D must be specified, too\n"
597 "   -p,--path-to-home path  use specified path instead of user account home dir\n"
598 "                           or /home prefix\n"
599 "   -m,--no-mount           don't use mount points for home dir\n"
600 "   -U,--unix userlist      additionally print UNIX users when using -l or -L\n"
601 "                           on a UNIX Samba server\n"
602 "                           userlist is a comma-separated list of usernames\n"
603 "                           or uid ranges (root,-25,50-100).\n"
604 "                           (enumerating large ranges can take a long time!)\n"
605 "   -s,--no-sids            (ignored)\n"
606 "   -g,--local-groups       (ignored)\n"
607 "   -h,--help               displays this message\n"
608 "   -v,--version            version information and exit\n"
609 "\n"
610 "Default is to print local accounts on stand-alone machines, domain accounts\n"
611 "on domain controllers and domain member machines.\n");
612   return 1;
613 }
614
615 static struct option longopts[] = {
616   {"current", no_argument, NULL, 'c'},
617   {"Current", no_argument, NULL, 'C'},
618   {"domain", optional_argument, NULL, 'd'},
619   {"Domain", optional_argument, NULL, 'D'},
620   {"local-groups", no_argument, NULL, 'g'},
621   {"help", no_argument, NULL, 'h'},
622   {"local", optional_argument, NULL, 'l'},
623   {"Local", optional_argument, NULL, 'L'},
624   {"no-mount", no_argument, NULL, 'm'},
625   {"id-offset", required_argument, NULL, 'o'},
626   {"path-to-home", required_argument, NULL, 'p'},
627   {"no-sids", no_argument, NULL, 's'},
628   {"separator", required_argument, NULL, 'S'},
629   {"username", required_argument, NULL, 'u'},
630   {"unix", required_argument, NULL, 'U'},
631   {"version", no_argument, NULL, 'v'},
632   {0, no_argument, NULL, 0}
633 };
634
635 static char opts[] = "cCd::D::ghl::L::mo:sS:p:u:U:v";
636
637 static void
638 print_version ()
639 {
640   const char *v = strchr (version, ':');
641   int len;
642   if (!v)
643     {
644       v = "?";
645       len = 1;
646     }
647   else
648     {
649       v += 2;
650       len = strchr (v, ' ') - v;
651     }
652   printf ("\
653 mkpasswd (cygwin) %.*s\n\
654 passwd File Generator\n\
655 Copyright 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2005, 2006, 2008 Red Hat, Inc.\n\
656 Compiled on %s\n\
657 ", len, v, __DATE__);
658 }
659
660 static void
661 enum_std_accounts ()
662 {
663   /* Generate service starter account entries. */
664   printf ("SYSTEM:*:18:544:,S-1-5-18::\n");
665   printf ("LocalService:*:19:544:U-NT AUTHORITY\\LocalService,S-1-5-19::\n");
666   printf ("NetworkService:*:20:544:U-NT AUTHORITY\\NetworkService,S-1-5-20::\n");
667   /* Get 'administrators' group (has localized name). */
668   print_special (&sid_nt_auth, 2, SECURITY_BUILTIN_DOMAIN_RID,
669                  DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0);
670 }
671
672 static PPOLICY_PRIMARY_DOMAIN_INFO p_dom;
673
674 static BOOL
675 fetch_primary_domain ()
676 {
677   NTSTATUS status;
678   LSA_OBJECT_ATTRIBUTES oa = { 0, 0, 0, 0, 0, 0 };
679   LSA_HANDLE lsa;
680
681   if (!p_dom)
682     {
683       status = LsaOpenPolicy (NULL, &oa, POLICY_VIEW_LOCAL_INFORMATION, &lsa);
684       if (!NT_SUCCESS (status))
685         return FALSE;
686       status = LsaQueryInformationPolicy (lsa, PolicyPrimaryDomainInformation,
687                                           (PVOID *) ((void *) &p_dom));
688       LsaClose (lsa);
689       if (!NT_SUCCESS (status))
690         return FALSE;
691     }
692   return !!p_dom->Sid;
693 }
694
695 int
696 main (int argc, char **argv)
697 {
698   int print_domlist = 0;
699   domlist_t domlist[32];
700   char *opt, *p, *ep;
701   int print_cygpath = 1;
702   int print_current = 0;
703   char *print_unix = NULL;
704   const char *sep_char = "\\";
705   DWORD id_offset = 10000, off;
706   int c, i;
707   char *disp_username = NULL;
708   char passed_home_path[PATH_MAX];
709   BOOL in_domain;
710   int optional_args = 0;
711
712   passed_home_path[0] = '\0';
713   if (!isatty (1))
714     setmode (1, O_BINARY);
715
716   /* Use locale from environment.  If not set or set to "C", use UTF-8. */
717   setlocale (LC_CTYPE, "");
718   if (!strcmp (setlocale (LC_CTYPE, NULL), "C"))
719     setlocale (LC_CTYPE, "en_US.UTF-8");
720   load_dsgetdcname ();
721   in_domain = fetch_primary_domain ();
722   fetch_current_user_sid ();
723
724   if (argc == 1)
725     {
726       enum_std_accounts ();
727       if (in_domain)
728         enum_users (TRUE, NULL, sep_char, print_cygpath, passed_home_path,
729                     10000, disp_username, 0);
730       else
731         enum_users (FALSE, NULL, sep_char, print_cygpath, passed_home_path, 0,
732                     disp_username, 0);
733       return 0;
734     }
735
736   unsetenv ("POSIXLY_CORRECT"); /* To get optional arg processing right. */
737   while ((c = getopt_long (argc, argv, opts, longopts, NULL)) != EOF)
738     switch (c)
739       {
740       case 'd':
741       case 'D':
742       case 'l':
743       case 'L':
744         if (print_domlist >= 32)
745           {
746             fprintf (stderr, "%s: Can not enumerate from more than 32 "
747                              "domains and machines.\n", __progname);
748             return 1;
749           }
750         domlist[print_domlist].domain = (c == 'd' || c == 'D');
751         opt = optarg ?:
752               argv[optind] && argv[optind][0] != '-' ? argv[optind] : NULL;
753         if (argv[optind] && opt == argv[optind])
754           ++optional_args;
755         for (i = 0; i < print_domlist; ++i)
756           if (domlist[i].domain == domlist[print_domlist].domain
757               && ((!domlist[i].str && !opt)
758                   || (domlist[i].str && opt
759                       && (off = strlen (domlist[i].str))
760                       && !strncmp (domlist[i].str, opt, off)
761                       && (!opt[off] || opt[off] == ','))))
762             {
763               fprintf (stderr, "%s: Duplicate %s '%s'.  Skipping...\n",
764                        __progname, domlist[i].domain ? "domain" : "machine",
765                        domlist[i].str);
766               goto skip;
767             }
768         domlist[print_domlist].str = opt;
769         domlist[print_domlist].id_offset = ULONG_MAX;
770         if (opt && (p = strchr (opt, ',')))
771           {
772             if (p == opt
773                 || !isdigit ((unsigned char) p[1])
774                 || (domlist[print_domlist].id_offset = strtol (p + 1, &ep, 10)
775                     , *ep))
776               {
777                 fprintf (stderr, "%s: Malformed domain,offset string '%s'.  "
778                          "Skipping...\n", __progname, opt);
779                 break;
780               }
781             *p = '\0';
782           }
783         domlist[print_domlist++].with_dom = (c == 'D' || c == 'L');
784 skip:
785         break;
786       case 'S':
787         sep_char = optarg;
788         if (strlen (sep_char) > 1)
789           {
790             fprintf (stderr, "%s: Only one character allowed as domain\\user "
791                              "separator character.\n", __progname);
792             return 1;
793           }
794         if (*sep_char == ':')
795           {
796             fprintf (stderr, "%s: Colon not allowed as domain\\user separator "
797                              "character.\n", __progname);
798             return 1;
799           }
800         break;
801       case 'U':
802         print_unix = optarg;
803         break;
804       case 'c':
805         sep_char = NULL;
806         /*FALLTHRU*/
807       case 'C':
808         print_current = 1;
809         break;
810       case 'o':
811         id_offset = strtoul (optarg, &ep, 10);
812         if (*ep)
813           {
814             fprintf (stderr, "%s: Malformed offset '%s'.  "
815                      "Skipping...\n", __progname, optarg);
816             return 1;
817           }
818         break;
819       case 'g':
820         break;
821       case 's':
822         break;
823       case 'm':
824         print_cygpath = 0;
825         break;
826       case 'p':
827         if (optarg[0] != '/')
828         {
829           fprintf (stderr, "%s: '%s' is not a fully qualified path.\n",
830                    __progname, optarg);
831           return 1;
832         }
833         strcpy (passed_home_path, optarg);
834         if (optarg[strlen (optarg)-1] != '/')
835           strcat (passed_home_path, "/");
836         break;
837       case 'u':
838         disp_username = optarg;
839         break;
840       case 'h':
841         usage (stdout);
842         return 0;
843       case 'v':
844         print_version ();
845         return 0;
846       default:
847         fprintf (stderr, "Try '%s --help' for more information.\n", __progname);
848         return 1;
849       }
850
851   optind += optional_args;
852   if (argv[optind])
853     {
854       fprintf (stderr,
855                "mkpasswd: non-option command line argument `%s' is not allowed.\n"
856                "Try `mkpasswd --help' for more information.\n", argv[optind]);
857       exit (1);
858     }
859
860   off = id_offset;
861   for (i = 0; i < print_domlist; ++i)
862     {
863       DWORD my_off = (domlist[i].domain || domlist[i].str)
864                      ? domlist[i].id_offset != ULONG_MAX
865                        ? domlist[i].id_offset : off : 0;
866       if (!domlist[i].domain && domlist[i].str && print_unix)
867         enum_unix_users (domlist + i, sep_char, my_off, print_unix);
868       if (!my_off && !disp_username)
869         enum_std_accounts ();
870       enum_users (domlist[i].domain, domlist + i, sep_char, print_cygpath,
871                   passed_home_path, my_off, disp_username, print_current);
872       if (my_off)
873         off += id_offset;
874     }
875
876   if (print_current && !got_curr_user)
877     current_user (print_cygpath, sep_char, passed_home_path, off,
878                   disp_username);
879
880   return 0;
881 }