OSDN Git Service

2002-03-01 David O'Brien <obrien@FreeBSD.org>
[pf3gnuchains/pf3gnuchains3x.git] / winsup / cygwin / passwd.cc
1 /* passwd.cc: getpwnam () and friends
2
3    Copyright 1996, 1997, 1998, 2001, 2002 Red Hat, Inc.
4
5 This file is part of Cygwin.
6
7 This software is a copyrighted work licensed under the terms of the
8 Cygwin license.  Please consult the file "CYGWIN_LICENSE" for
9 details. */
10
11 #include "winsup.h"
12 #include <stdlib.h>
13 #include <pwd.h>
14 #include <stdio.h>
15 #include <errno.h>
16 #include "cygerrno.h"
17 #include "security.h"
18 #include "fhandler.h"
19 #include "path.h"
20 #include "dtable.h"
21 #include "sync.h"
22 #include "sigproc.h"
23 #include "pinfo.h"
24 #include "cygheap.h"
25 #include <sys/termios.h>
26 #include "pwdgrp.h"
27
28 /* Read /etc/passwd only once for better performance.  This is done
29    on the first call that needs information from it. */
30
31 static struct passwd *passwd_buf;       /* passwd contents in memory */
32 static int curr_lines;
33 static int max_lines;
34
35 static pwdgrp_check passwd_state;
36
37
38 /* Position in the passwd cache */
39 #ifdef _MT_SAFE
40 #define pw_pos  _reent_winsup ()->_pw_pos
41 #else
42 static int pw_pos = 0;
43 #endif
44
45 /* Remove a : teminated string from the buffer, and increment the pointer */
46 static char *
47 grab_string (char **p)
48 {
49   char *src = *p;
50   char *res = src;
51
52   while (*src && *src != ':' && *src != '\n')
53     src++;
54
55   if (*src == ':')
56     {
57       *src = 0;
58       src++;
59     }
60   *p = src;
61   return res;
62 }
63
64 /* same, for ints */
65 static int
66 grab_int (char **p)
67 {
68   char *src = *p;
69   int val = strtol (src, NULL, 10);
70   while (*src && *src != ':' && *src != '\n')
71     src++;
72   if (*src == ':')
73     src++;
74   *p = src;
75   return val;
76 }
77
78 /* Parse /etc/passwd line into passwd structure. */
79 void
80 parse_pwd (struct passwd &res, char *buf)
81 {
82   /* Allocate enough room for the passwd struct and all the strings
83      in it in one go */
84   size_t len = strlen (buf);
85   char *mybuf = (char *) malloc (len + 1);
86   (void) memcpy (mybuf, buf, len + 1);
87   if (mybuf[--len] == '\n')
88     mybuf[len] = '\0';
89
90   res.pw_name = grab_string (&mybuf);
91   res.pw_passwd = grab_string (&mybuf);
92   res.pw_uid = grab_int (&mybuf);
93   res.pw_gid = grab_int (&mybuf);
94   res.pw_comment = 0;
95   res.pw_gecos = grab_string (&mybuf);
96   res.pw_dir =  grab_string (&mybuf);
97   res.pw_shell = grab_string (&mybuf);
98 }
99
100 /* Add one line from /etc/passwd into the password cache */
101 static void
102 add_pwd_line (char *line)
103 {
104     if (curr_lines >= max_lines)
105       {
106         max_lines += 10;
107         passwd_buf = (struct passwd *) realloc (passwd_buf, max_lines * sizeof (struct passwd));
108       }
109     parse_pwd (passwd_buf[curr_lines++], line);
110 }
111
112 class passwd_lock
113 {
114   bool armed;
115   static NO_COPY pthread_mutex_t mutex;
116  public:
117   passwd_lock (bool doit)
118   {
119     if (doit)
120       pthread_mutex_lock (&mutex);
121     armed = doit;
122   }
123   ~passwd_lock ()
124   {
125     if (armed)
126       pthread_mutex_unlock (&mutex);
127   }
128 };
129
130 pthread_mutex_t NO_COPY passwd_lock::mutex = (pthread_mutex_t) PTHREAD_MUTEX_INITIALIZER;
131
132 /* Read in /etc/passwd and save contents in the password cache.
133    This sets passwd_state to loaded or emulated so functions in this file can
134    tell that /etc/passwd has been read in or will be emulated. */
135 void
136 read_etc_passwd ()
137 {
138     char linebuf[1024];
139     /* A mutex is ok for speed here - pthreads will use critical sections not mutex's
140      * for non-shared mutexs in the future. Also, this function will at most be called
141      * once from each thread, after that the passwd_state test will succeed
142      */
143     passwd_lock here (cygwin_finished_initializing);
144
145     /* if we got blocked by the mutex, then etc_passwd may have been processed */
146     if (passwd_state != uninitialized)
147       return;
148
149     if (passwd_state != initializing)
150       {
151         passwd_state = initializing;
152         if (max_lines) /* When rereading, free allocated memory first. */
153           {
154             for (int i = 0; i < curr_lines; ++i)
155               free (passwd_buf[i].pw_name);
156             curr_lines = 0;
157           }
158
159         FILE *f = fopen ("/etc/passwd", "rt");
160
161         if (f)
162           {
163             while (fgets (linebuf, sizeof (linebuf), f) != NULL)
164               {
165                 if (strlen (linebuf))
166                   add_pwd_line (linebuf);
167               }
168
169             passwd_state.set_last_modified (f);
170             fclose (f);
171             passwd_state = loaded;
172           }
173         else
174           {
175             debug_printf ("Emulating /etc/passwd");
176             snprintf (linebuf, sizeof (linebuf), "%s::%u:%u::%s:/bin/sh", cygheap->user.name (),
177                       (unsigned) DEFAULT_UID, (unsigned) DEFAULT_GID, getenv ("HOME") ?: "/");
178             add_pwd_line (linebuf);
179             passwd_state = emulated;
180           }
181
182       }
183
184   return;
185 }
186
187 /* Cygwin internal */
188 /* If this ever becomes non-reentrant, update all the getpw*_r functions */
189 static struct passwd *
190 search_for (__uid16_t uid, const char *name)
191 {
192   struct passwd *res = 0;
193   struct passwd *default_pw = 0;
194
195   for (int i = 0; i < curr_lines; i++)
196     {
197       res = passwd_buf + i;
198       if (res->pw_uid == DEFAULT_UID)
199         default_pw = res;
200       /* on Windows NT user names are case-insensitive */
201       if (name)
202         {
203           if (strcasematch (name, res->pw_name))
204             return res;
205         }
206       else if (uid == res->pw_uid)
207         return res;
208     }
209
210   /* Return default passwd entry if passwd is emulated or it's a
211      request for the current user. */
212   if (passwd_state != loaded
213       || (!name && uid == myself->uid)
214       || (name && strcasematch (name, cygheap->user.name ())))
215     return default_pw;
216
217   return NULL;
218 }
219
220 extern "C" struct passwd *
221 getpwuid (__uid16_t uid)
222 {
223   if (passwd_state  <= initializing)
224     read_etc_passwd ();
225
226   pthread_testcancel();
227
228   return search_for (uid, 0);
229 }
230
231 extern "C" int
232 getpwuid_r (__uid16_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result)
233 {
234   *result = NULL;
235
236   if (!pwd || !buffer)
237     return ERANGE;
238
239   if (passwd_state  <= initializing)
240     read_etc_passwd ();
241
242   pthread_testcancel();
243
244   struct passwd *temppw = search_for (uid, 0);
245
246   if (!temppw)
247     return 0;
248
249   /* check needed buffer size. */
250   size_t needsize = strlen (temppw->pw_name) + strlen (temppw->pw_dir) +
251                     strlen (temppw->pw_shell) + strlen (temppw->pw_gecos) +
252                     strlen (temppw->pw_passwd) + 5;
253   if (needsize > bufsize)
254     return ERANGE;
255
256   /* make a copy of temppw */
257   *result = pwd;
258   pwd->pw_uid = temppw->pw_uid;
259   pwd->pw_gid = temppw->pw_gid;
260   pwd->pw_name = buffer;
261   pwd->pw_dir = pwd->pw_name + strlen (temppw->pw_name) + 1;
262   pwd->pw_shell = pwd->pw_dir + strlen (temppw->pw_dir) + 1;
263   pwd->pw_gecos = pwd->pw_shell + strlen (temppw->pw_shell) + 1;
264   pwd->pw_passwd = pwd->pw_gecos + strlen (temppw->pw_gecos) + 1;
265   strcpy (pwd->pw_name, temppw->pw_name);
266   strcpy (pwd->pw_dir, temppw->pw_dir);
267   strcpy (pwd->pw_shell, temppw->pw_shell);
268   strcpy (pwd->pw_gecos, temppw->pw_gecos);
269   strcpy (pwd->pw_passwd, temppw->pw_passwd);
270   return 0;
271 }
272
273 extern "C" struct passwd *
274 getpwnam (const char *name)
275 {
276   if (passwd_state  <= initializing)
277     read_etc_passwd ();
278
279   pthread_testcancel();
280
281   return search_for (0, name);
282 }
283
284
285 /* the max size buffer we can expect to
286  * use is returned via sysconf with _SC_GETPW_R_SIZE_MAX.
287  * This may need updating! - Rob Collins April 2001.
288  */
289 extern "C" int
290 getpwnam_r (const char *nam, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result)
291 {
292   *result = NULL;
293
294   if (!pwd || !buffer || !nam)
295     return ERANGE;
296
297   if (passwd_state  <= initializing)
298     read_etc_passwd ();
299
300   pthread_testcancel();
301
302   struct passwd *temppw = search_for (0, nam);
303
304   if (!temppw)
305     return 0;
306
307   /* check needed buffer size. */
308   size_t needsize = strlen (temppw->pw_name) + strlen (temppw->pw_dir) +
309                     strlen (temppw->pw_shell) + strlen (temppw->pw_gecos) +
310                     strlen (temppw->pw_passwd) + 5;
311   if (needsize > bufsize)
312     return ERANGE;
313
314   /* make a copy of temppw */
315   *result = pwd;
316   pwd->pw_uid = temppw->pw_uid;
317   pwd->pw_gid = temppw->pw_gid;
318   pwd->pw_name = buffer;
319   pwd->pw_dir = pwd->pw_name + strlen (temppw->pw_name) + 1;
320   pwd->pw_shell = pwd->pw_dir + strlen (temppw->pw_dir) + 1;
321   pwd->pw_gecos = pwd->pw_shell + strlen (temppw->pw_shell) + 1;
322   pwd->pw_passwd = pwd->pw_gecos + strlen (temppw->pw_gecos) + 1;
323   strcpy (pwd->pw_name, temppw->pw_name);
324   strcpy (pwd->pw_dir, temppw->pw_dir);
325   strcpy (pwd->pw_shell, temppw->pw_shell);
326   strcpy (pwd->pw_gecos, temppw->pw_gecos);
327   strcpy (pwd->pw_passwd, temppw->pw_passwd);
328   return 0;
329 }
330
331 extern "C" struct passwd *
332 getpwent (void)
333 {
334   if (passwd_state  <= initializing)
335     read_etc_passwd ();
336
337   if (pw_pos < curr_lines)
338     return passwd_buf + pw_pos++;
339
340   return NULL;
341 }
342
343 extern "C" struct passwd *
344 getpwduid (__uid16_t)
345 {
346   return NULL;
347 }
348
349 extern "C" void
350 setpwent (void)
351 {
352   pw_pos = 0;
353 }
354
355 extern "C" void
356 endpwent (void)
357 {
358   pw_pos = 0;
359 }
360
361 extern "C" int
362 setpassent ()
363 {
364   return 0;
365 }
366
367 /* Internal function. ONLY USE THIS INTERNALLY, NEVER `getpwent'!!! */
368 struct passwd *
369 internal_getpwent (int pos)
370 {
371   if (passwd_state  <= initializing)
372     read_etc_passwd ();
373
374   if (pos < curr_lines)
375     return passwd_buf + pos;
376   return NULL;
377 }
378
379 extern "C" char *
380 getpass (const char * prompt)
381 {
382 #ifdef _MT_SAFE
383   char *pass=_reent_winsup ()->_pass;
384 #else
385   static char pass[_PASSWORD_LEN];
386 #endif
387   struct termios ti, newti;
388
389   if (passwd_state  <= initializing)
390     read_etc_passwd ();
391
392   cygheap_fdget fhstdin (0);
393
394   if (fhstdin < 0)
395     pass[0] = '\0';
396   else
397     {
398       fhstdin->tcgetattr (&ti);
399       newti = ti;
400       newti.c_lflag &= ~ECHO;
401       fhstdin->tcsetattr (TCSANOW, &newti);
402       fputs (prompt, stderr);
403       fgets (pass, _PASSWORD_LEN, stdin);
404       fprintf (stderr, "\n");
405       for (int i=0; pass[i]; i++)
406         if (pass[i] == '\r' || pass[i] == '\n')
407           pass[i] = '\0';
408       fhstdin->tcsetattr (TCSANOW, &ti);
409     }
410   return pass;
411 }