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