OSDN Git Service

* include/sys/cygwin.h: Add new cygwin_getinfo_type
[pf3gnuchains/pf3gnuchains4x.git] / winsup / cygwin / uinfo.cc
1 /* uinfo.cc: user info (uid, gid, etc...)
2
3    Copyright 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
4    2006, 2007, 2008 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 #include "winsup.h"
13 #include <unistd.h>
14 #include <wininet.h>
15 #include <stdlib.h>
16 #include <wchar.h>
17 #include <lm.h>
18 #include <iptypes.h>
19 #include <sys/cygwin.h>
20 #include "cygerrno.h"
21 #include "pinfo.h"
22 #include "path.h"
23 #include "fhandler.h"
24 #include "dtable.h"
25 #include "cygheap.h"
26 #include "shared_info.h"
27 #include "registry.h"
28 #include "child_info.h"
29 #include "environ.h"
30 #include "pwdgrp.h"
31 #include "tls_pbuf.h"
32 #include "ntdll.h"
33
34 /* Initialize the part of cygheap_user that does not depend on files.
35    The information is used in shared.cc for the user shared.
36    Final initialization occurs in uinfo_init */
37 void
38 cygheap_user::init ()
39 {
40   char user_name[UNLEN + 1];
41   DWORD user_name_len = UNLEN + 1;
42
43   set_name (GetUserName (user_name, &user_name_len) ? user_name : "unknown");
44
45   DWORD siz;
46   PSECURITY_DESCRIPTOR psd;
47
48   if (!GetTokenInformation (hProcToken, TokenPrimaryGroup,
49                             &groups.pgsid, sizeof (cygsid), &siz))
50     system_printf ("GetTokenInformation (TokenPrimaryGroup), %E");
51
52   /* Get the SID from current process and store it in effec_cygsid */
53   if (!GetTokenInformation (hProcToken, TokenUser, &effec_cygsid,
54                             sizeof (cygsid), &siz))
55     {
56       system_printf ("GetTokenInformation (TokenUser), %E");
57       return;
58     }
59
60   /* Set token owner to the same value as token user */
61   if (!SetTokenInformation (hProcToken, TokenOwner, &effec_cygsid,
62                             sizeof (cygsid)))
63     debug_printf ("SetTokenInformation(TokenOwner), %E");
64
65   /* Standard way to build a security descriptor with the usual DACL */
66   PSECURITY_ATTRIBUTES sa_buf = (PSECURITY_ATTRIBUTES) alloca (1024);
67   psd = (PSECURITY_DESCRIPTOR)
68                 (sec_user_nih (sa_buf, sid()))->lpSecurityDescriptor;
69
70   BOOL acl_exists, dummy;
71   TOKEN_DEFAULT_DACL dacl;
72   if (GetSecurityDescriptorDacl (psd, &acl_exists, &dacl.DefaultDacl, &dummy)
73       && acl_exists && dacl.DefaultDacl)
74     {
75       NTSTATUS status;
76
77       /* Set the default DACL and the process DACL */
78       if (!SetTokenInformation (hProcToken, TokenDefaultDacl, &dacl,
79                                 sizeof (dacl)))
80         system_printf ("SetTokenInformation (TokenDefaultDacl), %E");
81       if ((status = NtSetSecurityObject (hMainProc, DACL_SECURITY_INFORMATION,
82                                          psd)))
83         system_printf ("NtSetSecurityObject, %lx", status);
84     }
85   else
86     system_printf("Cannot get dacl, %E");
87 }
88
89 void
90 internal_getlogin (cygheap_user &user)
91 {
92   struct passwd *pw = NULL;
93
94   cygpsid psid = user.sid ();
95   pw = internal_getpwsid (psid);
96
97   if (!pw && !(pw = internal_getpwnam (user.name ()))
98       && !(pw = internal_getpwuid (DEFAULT_UID)))
99     debug_printf ("user not found in augmented /etc/passwd");
100   else
101     {
102       cygsid gsid;
103
104       myself->uid = pw->pw_uid;
105       myself->gid = pw->pw_gid;
106       user.set_name (pw->pw_name);
107       if (gsid.getfromgr (internal_getgrgid (pw->pw_gid)))
108         {
109           if (gsid != user.groups.pgsid)
110             {
111               /* Set primary group to the group in /etc/passwd. */
112               if (!SetTokenInformation (hProcToken, TokenPrimaryGroup,
113                                         &gsid, sizeof gsid))
114                 debug_printf ("SetTokenInformation(TokenPrimaryGroup), %E");
115               else
116                 user.groups.pgsid = gsid;
117               clear_procimptoken ();
118             }
119         }
120       else
121         debug_printf ("gsid not found in augmented /etc/group");
122     }
123   cygheap->user.ontherange (CH_HOME, pw);
124 }
125
126 void
127 uinfo_init ()
128 {
129   if (child_proc_info && !cygheap->user.has_impersonation_tokens ())
130     return;
131
132   if (!child_proc_info)
133     internal_getlogin (cygheap->user); /* Set the cygheap->user. */
134   /* Conditions must match those in spawn to allow starting child
135      processes with ruid != euid and rgid != egid. */
136   else if (cygheap->user.issetuid ()
137            && cygheap->user.saved_uid == cygheap->user.real_uid
138            && cygheap->user.saved_gid == cygheap->user.real_gid
139            && !cygheap->user.groups.issetgroups ()
140            && !cygheap->user.setuid_to_restricted)
141     {
142       cygheap->user.reimpersonate ();
143       return;
144     }
145   else
146     cygheap->user.close_impersonation_tokens ();
147
148   cygheap->user.saved_uid = cygheap->user.real_uid = myself->uid;
149   cygheap->user.saved_gid = cygheap->user.real_gid = myself->gid;
150   cygheap->user.external_token = NO_IMPERSONATION;
151   cygheap->user.internal_token = NO_IMPERSONATION;
152   cygheap->user.curr_primary_token = NO_IMPERSONATION;
153   cygheap->user.curr_imp_token = NO_IMPERSONATION;
154   cygheap->user.ext_token_is_restricted = false;
155   cygheap->user.curr_token_is_restricted = false;
156   cygheap->user.setuid_to_restricted = false;
157   cygheap->user.set_saved_sid ();       /* Update the original sid */
158   cygheap->user.reimpersonate ();
159 }
160
161 extern "C" int
162 getlogin_r (char *name, size_t namesize)
163 {
164   char *login = getlogin ();
165   size_t len = strlen (login) + 1;
166   if (len > namesize)
167     return ERANGE;
168   myfault efault;
169   if (efault.faulted ())
170     return EFAULT;
171   strncpy (name, login, len);
172   return 0;
173 }
174
175 extern "C" char *
176 getlogin (void)
177 {
178   return strcpy (_my_tls.locals.username, cygheap->user.name ());
179 }
180
181 extern "C" __uid32_t
182 getuid32 (void)
183 {
184   return cygheap->user.real_uid;
185 }
186
187 extern "C" __uid16_t
188 getuid (void)
189 {
190   return cygheap->user.real_uid;
191 }
192
193 extern "C" __gid32_t
194 getgid32 (void)
195 {
196   return cygheap->user.real_gid;
197 }
198
199 extern "C" __gid16_t
200 getgid (void)
201 {
202   return cygheap->user.real_gid;
203 }
204
205 extern "C" __uid32_t
206 geteuid32 (void)
207 {
208   return myself->uid;
209 }
210
211 extern "C" __uid16_t
212 geteuid (void)
213 {
214   return myself->uid;
215 }
216
217 extern "C" __gid32_t
218 getegid32 (void)
219 {
220   return myself->gid;
221 }
222
223 extern "C" __gid16_t
224 getegid (void)
225 {
226   return myself->gid;
227 }
228
229 /* Not quite right - cuserid can change, getlogin can't */
230 extern "C" char *
231 cuserid (char *src)
232 {
233   if (!src)
234     return getlogin ();
235
236   strcpy (src, getlogin ());
237   return src;
238 }
239
240 const char *
241 cygheap_user::ontherange (homebodies what, struct passwd *pw)
242 {
243   LPUSER_INFO_3 ui = NULL;
244   WCHAR wuser[UNLEN + 1];
245   NET_API_STATUS ret;
246   char homedrive_env_buf[3];
247   char *newhomedrive = NULL;
248   char *newhomepath = NULL;
249   tmp_pathbuf tp;
250
251   debug_printf ("what %d, pw %p", what, pw);
252   if (what == CH_HOME)
253     {
254       char *p;
255
256       if ((p = getenv ("HOME")))
257         debug_printf ("HOME is already in the environment %s", p);
258       else
259         {
260           if (pw && pw->pw_dir && *pw->pw_dir)
261             {
262               debug_printf ("Set HOME (from /etc/passwd) to %s", pw->pw_dir);
263               setenv ("HOME", pw->pw_dir, 1);
264             }
265           else
266             {
267               char home[strlen (name ()) + 8];
268
269               debug_printf ("Set HOME to default /home/USER");
270               __small_sprintf (home, "/home/%s", name ());
271               setenv ("HOME", home, 1);
272             }
273         }
274     }
275
276   if (what != CH_HOME && homepath == NULL && newhomepath == NULL)
277     {
278       char *homepath_env_buf = tp.c_get ();
279       if (!pw)
280         pw = internal_getpwnam (name ());
281       if (pw && pw->pw_dir && *pw->pw_dir)
282         cygwin_conv_path (CCP_POSIX_TO_WIN_A, pw->pw_dir, homepath_env_buf,
283                           NT_MAX_PATH);
284       else
285         {
286           homepath_env_buf[0] = homepath_env_buf[1] = '\0';
287           if (logsrv ())
288             {
289               WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3];
290               sys_mbstowcs (wlogsrv, sizeof (wlogsrv) / sizeof (*wlogsrv),
291                             logsrv ());
292              sys_mbstowcs (wuser, sizeof (wuser) / sizeof (*wuser), winname ());
293               if (!(ret = NetUserGetInfo (wlogsrv, wuser, 3, (LPBYTE *) &ui)))
294                 {
295                   sys_wcstombs (homepath_env_buf, NT_MAX_PATH,
296                                 ui->usri3_home_dir);
297                   if (!homepath_env_buf[0])
298                     {
299                       sys_wcstombs (homepath_env_buf, NT_MAX_PATH,
300                                     ui->usri3_home_dir_drive);
301                       if (homepath_env_buf[0])
302                         strcat (homepath_env_buf, "\\");
303                       else
304                         cygwin_conv_path (CCP_POSIX_TO_WIN_A | CCP_ABSOLUTE,
305                                           "/", homepath_env_buf, NT_MAX_PATH);
306                     }
307                 }
308             }
309           if (ui)
310             NetApiBufferFree (ui);
311         }
312
313       if (homepath_env_buf[1] != ':')
314         {
315           newhomedrive = almost_null;
316           newhomepath = homepath_env_buf;
317         }
318       else
319         {
320           homedrive_env_buf[0] = homepath_env_buf[0];
321           homedrive_env_buf[1] = homepath_env_buf[1];
322           homedrive_env_buf[2] = '\0';
323           newhomedrive = homedrive_env_buf;
324           newhomepath = homepath_env_buf + 2;
325         }
326     }
327
328   if (newhomedrive && newhomedrive != homedrive)
329     cfree_and_set (homedrive, (newhomedrive == almost_null)
330                               ? almost_null : cstrdup (newhomedrive));
331
332   if (newhomepath && newhomepath != homepath)
333     cfree_and_set (homepath, cstrdup (newhomepath));
334
335   switch (what)
336     {
337     case CH_HOMEDRIVE:
338       return homedrive;
339     case CH_HOMEPATH:
340       return homepath;
341     default:
342       return homepath;
343     }
344 }
345
346 const char *
347 cygheap_user::test_uid (char *&what, const char *name, size_t namelen)
348 {
349   if (!what && !issetuid ())
350     what = getwinenveq (name, namelen, HEAP_STR);
351   return what;
352 }
353
354 const char *
355 cygheap_user::env_logsrv (const char *name, size_t namelen)
356 {
357   if (test_uid (plogsrv, name, namelen))
358     return plogsrv;
359
360   const char *mydomain = domain ();
361   const char *myname = winname ();
362   if (!mydomain || ascii_strcasematch (myname, "SYSTEM"))
363     return almost_null;
364
365   WCHAR wdomain[MAX_DOMAIN_NAME_LEN + 1];
366   WCHAR wlogsrv[INTERNET_MAX_HOST_NAME_LENGTH + 3];
367   sys_mbstowcs (wdomain, MAX_DOMAIN_NAME_LEN + 1, mydomain);
368   cfree_and_set (plogsrv, almost_null);
369   if (get_logon_server (wdomain, wlogsrv, false))
370     sys_wcstombs_alloc (&plogsrv, HEAP_STR, wlogsrv);
371   return plogsrv;
372 }
373
374 const char *
375 cygheap_user::env_domain (const char *name, size_t namelen)
376 {
377   if (pwinname && test_uid (pdomain, name, namelen))
378     return pdomain;
379
380   char username[UNLEN + 1];
381   DWORD ulen = sizeof (username);
382   char userdomain[DNLEN + 1];
383   DWORD dlen = sizeof (userdomain);
384   SID_NAME_USE use;
385
386   cfree_and_set (pwinname, almost_null);
387   cfree_and_set (pdomain, almost_null);
388   if (!LookupAccountSid (NULL, sid (), username, &ulen,
389                          userdomain, &dlen, &use))
390     __seterrno ();
391   else
392     {
393       pwinname = cstrdup (username);
394       pdomain = cstrdup (userdomain);
395     }
396   return pdomain;
397 }
398
399 const char *
400 cygheap_user::env_userprofile (const char *name, size_t namelen)
401 {
402   if (test_uid (puserprof, name, namelen))
403     return puserprof;
404
405   WCHAR userprofile_env_buf[NT_MAX_PATH];
406   WCHAR win_id[UNLEN + 1]; /* Large enough for SID */
407
408   cfree_and_set (puserprof, almost_null);
409   if (get_registry_hive_path (get_windows_id (win_id), userprofile_env_buf))
410     sys_wcstombs_alloc (&puserprof, HEAP_STR, userprofile_env_buf);
411
412   return puserprof;
413 }
414
415 const char *
416 cygheap_user::env_homepath (const char *name, size_t namelen)
417 {
418   return ontherange (CH_HOMEPATH);
419 }
420
421 const char *
422 cygheap_user::env_homedrive (const char *name, size_t namelen)
423 {
424   return ontherange (CH_HOMEDRIVE);
425 }
426
427 const char *
428 cygheap_user::env_name (const char *name, size_t namelen)
429 {
430   if (!test_uid (pwinname, name, namelen))
431     domain ();
432   return pwinname;
433 }
434
435 const char *
436 cygheap_user::env_systemroot (const char *name, size_t namelen)
437 {
438   if (!psystemroot)
439     {
440       int size = GetWindowsDirectory (NULL, 0);
441       if (size > 0)
442         {
443           psystemroot = (char *) cmalloc_abort (HEAP_STR, ++size);
444           size = GetWindowsDirectory (psystemroot, size);
445           if (size <= 0)
446             {
447               cfree (psystemroot);
448               psystemroot = NULL;
449             }
450         }
451       if (size <= 0)
452         debug_printf ("GetWindowsDirectory(), %E");
453     }
454   return psystemroot;
455 }
456
457 char *
458 pwdgrp::next_str (char c)
459 {
460   char *res = lptr;
461   lptr = strechr (lptr, c);
462   if (*lptr)
463     *lptr++ = '\0';
464   return res;
465 }
466
467 bool
468 pwdgrp::next_num (unsigned long& n)
469 {
470   char *p = next_str (':');
471   char *cp;
472   n = strtoul (p, &cp, 10);
473   return p != cp && !*cp;
474 }
475
476 char *
477 pwdgrp::add_line (char *eptr)
478 {
479   if (eptr)
480     {
481       lptr = eptr;
482       eptr = strchr (lptr, '\n');
483       if (eptr)
484         {
485           if (eptr > lptr && eptr[-1] == '\r')
486             eptr[-1] = '\0';
487           else
488             *eptr = '\0';
489           eptr++;
490         }
491       if (curr_lines >= max_lines)
492         {
493           max_lines += 10;
494           *pwdgrp_buf = realloc (*pwdgrp_buf, max_lines * pwdgrp_buf_elem_size);
495         }
496       if ((this->*parse) ())
497         curr_lines++;
498     }
499   return eptr;
500 }
501
502 void
503 pwdgrp::load (const wchar_t *rel_path)
504 {
505   static const char failed[] = "failed";
506   static const char succeeded[] = "succeeded";
507   const char *res = failed;
508   HANDLE fh = NULL;
509
510   NTSTATUS status;
511   OBJECT_ATTRIBUTES attr;
512   IO_STATUS_BLOCK io;
513   FILE_STANDARD_INFORMATION fsi;
514
515   if (buf)
516     free (buf);
517   buf = NULL;
518   curr_lines = 0;
519
520   if (!path &&
521       !(path = (PWCHAR) malloc ((wcslen (cygwin_shared->installation_root)
522                                  + wcslen (rel_path) + 1) * sizeof (WCHAR))))
523     {
524       paranoid_printf ("malloc (%W) failed", rel_path);
525       goto out;
526     }
527   wcpcpy (wcpcpy (path, cygwin_shared->installation_root), rel_path);
528   RtlInitUnicodeString (&upath, path);
529
530   InitializeObjectAttributes (&attr, &upath, OBJ_CASE_INSENSITIVE, NULL, NULL);
531   etc_ix = etc::init (etc_ix, &attr);
532
533   paranoid_printf ("%S", &upath);
534
535   status = NtOpenFile (&fh, SYNCHRONIZE | FILE_READ_DATA, &attr, &io,
536                        FILE_SHARE_VALID_FLAGS,
537                        FILE_SYNCHRONOUS_IO_NONALERT
538                        | FILE_OPEN_FOR_BACKUP_INTENT);
539   if (!NT_SUCCESS (status))
540     {
541       paranoid_printf ("NtOpenFile(%S) failed, status %p", &upath, status);
542       goto out;
543     }
544   status = NtQueryInformationFile (fh, &io, &fsi, sizeof fsi,
545                                    FileStandardInformation);
546   if (!NT_SUCCESS (status))
547     {
548       paranoid_printf ("NtQueryInformationFile(%S) failed, status %p",
549                        &upath, status);
550       goto out;
551     }
552   /* FIXME: Should we test for HighPart set?  If so, the
553      passwd or group file is way beyond what we can handle. */
554   /* FIXME 2: It's still ugly that we keep the file in memory.
555      Big organizations have naturally large passwd files. */
556   buf = (char *) malloc (fsi.EndOfFile.LowPart + 1);
557   if (!buf)
558     {
559       paranoid_printf ("malloc (%d) failed", fsi.EndOfFile.LowPart);
560       goto out;
561     }
562   status = NtReadFile (fh, NULL, NULL, NULL, &io, buf, fsi.EndOfFile.LowPart,
563                        NULL, NULL);
564   if (!NT_SUCCESS (status))
565     {
566       paranoid_printf ("NtReadFile(%S) failed, status %p", &upath, status);
567       free (buf);
568       goto out;
569     }
570   buf[fsi.EndOfFile.LowPart] = '\0';
571   for (char *eptr = buf; (eptr = add_line (eptr)); )
572     continue;
573   debug_printf ("%W curr_lines %d", rel_path, curr_lines);
574   res = succeeded;
575
576 out:
577   if (fh)
578     NtClose (fh);
579   debug_printf ("%W load %s", rel_path, res);
580   initialized = true;
581 }