OSDN Git Service

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