Cygwin: add newgrp release notes
[newlib-cygwin.git] / winsup / cygwin / passwd.cc
blobb8457a46f963b549279b4507e919628a32b48e79
1 /* passwd.cc: getpwnam () and friends
3 This file is part of Cygwin.
5 This software is a copyrighted work licensed under the terms of the
6 Cygwin license. Please consult the file "CYGWIN_LICENSE" for
7 details. */
9 #include "winsup.h"
10 #include <lm.h>
11 #include <stdlib.h>
12 #include <stdio.h>
13 #include "cygerrno.h"
14 #include "security.h"
15 #include "path.h"
16 #include "fhandler.h"
17 #include "dtable.h"
18 #include "pinfo.h"
19 #include "cygheap.h"
20 #include "shared_info.h"
21 #include "miscfuncs.h"
22 #include "ldap.h"
23 #include "tls_pbuf.h"
25 /* Parse /etc/passwd line into passwd structure. */
26 bool
27 pwdgrp::parse_passwd ()
29 pg_pwd &res = passwd ()[curr_lines];
30 res.p.pw_name = next_str (':');
31 res.p.pw_passwd = next_str (':');
32 if (!next_num (res.p.pw_uid))
33 return false;
34 if (!next_num (res.p.pw_gid))
35 return false;
36 res.p.pw_comment = NULL;
37 res.p.pw_gecos = next_str (':');
38 res.p.pw_dir = next_str (':');
39 res.p.pw_shell = next_str (':');
40 cygsid csid;
41 if (csid.getfrompw_gecos (&res.p))
42 RtlCopySid (SECURITY_MAX_SID_SIZE, res.sid, csid);
43 /* lptr points to the \0 after pw_shell. Increment by one to get the correct
44 required buffer len in getpw_cp. */
45 res.len = lptr - res.p.pw_name + 1;
46 return true;
49 void
50 pwdgrp::init_pwd ()
52 pwdgrp_buf_elem_size = sizeof (pg_pwd);
53 parse = &pwdgrp::parse_passwd;
56 struct passwd *
57 pwdgrp::find_user (cygpsid &sid)
59 for (ULONG i = 0; i < curr_lines; i++)
60 if (sid == passwd ()[i].sid)
61 return &passwd ()[i].p;
62 return NULL;
65 struct passwd *
66 pwdgrp::find_user (const char *name)
68 for (ULONG i = 0; i < curr_lines; i++)
69 /* on Windows NT user names are case-insensitive */
70 if (strcasematch (name, passwd ()[i].p.pw_name))
71 return &passwd ()[i].p;
72 return NULL;
75 struct passwd *
76 pwdgrp::find_user (uid_t uid)
78 for (ULONG i = 0; i < curr_lines; i++)
79 if (uid == passwd ()[i].p.pw_uid)
80 return &passwd ()[i].p;
81 return NULL;
84 struct passwd *
85 internal_getpwsid (cygpsid &sid, cyg_ldap *pldap)
87 struct passwd *ret;
89 cygheap->pg.nss_init ();
90 /* Check caches first. */
91 if (cygheap->pg.nss_cygserver_caching ()
92 && (ret = cygheap->pg.pwd_cache.cygserver.find_user (sid)))
93 return ret;
94 if (cygheap->pg.nss_pwd_files ()
95 && (ret = cygheap->pg.pwd_cache.file.find_user (sid)))
96 return ret;
97 if (cygheap->pg.nss_pwd_db ()
98 && (ret = cygheap->pg.pwd_cache.win.find_user (sid)))
99 return ret;
100 /* Ask sources afterwards. */
101 if (cygheap->pg.nss_cygserver_caching ()
102 && (ret = cygheap->pg.pwd_cache.cygserver.add_user_from_cygserver (sid)))
103 return ret;
104 if (cygheap->pg.nss_pwd_files ())
106 cygheap->pg.pwd_cache.file.check_file ();
107 if ((ret = cygheap->pg.pwd_cache.file.add_user_from_file (sid)))
108 return ret;
110 if (cygheap->pg.nss_pwd_db ())
111 return cygheap->pg.pwd_cache.win.add_user_from_windows (sid, pldap);
112 return NULL;
115 /* This function gets only called from mkpasswd via cygwin_internal. */
116 struct passwd *
117 internal_getpwsid_from_db (cygpsid &sid)
119 cygheap->pg.nss_init ();
120 return cygheap->pg.pwd_cache.win.add_user_from_windows (sid);
123 struct passwd *
124 internal_getpwnam (const char *name, cyg_ldap *pldap)
126 struct passwd *ret;
128 cygheap->pg.nss_init ();
129 /* Check caches first. */
130 if (cygheap->pg.nss_cygserver_caching ()
131 && (ret = cygheap->pg.pwd_cache.cygserver.find_user (name)))
132 return ret;
133 if (cygheap->pg.nss_pwd_files ()
134 && (ret = cygheap->pg.pwd_cache.file.find_user (name)))
135 return ret;
136 if (cygheap->pg.nss_pwd_db ()
137 && (ret = cygheap->pg.pwd_cache.win.find_user (name)))
138 return ret;
139 /* Ask sources afterwards. */
140 if (cygheap->pg.nss_cygserver_caching ()
141 && (ret = cygheap->pg.pwd_cache.cygserver.add_user_from_cygserver (name)))
142 return ret;
143 if (cygheap->pg.nss_pwd_files ())
145 cygheap->pg.pwd_cache.file.check_file ();
146 if ((ret = cygheap->pg.pwd_cache.file.add_user_from_file (name)))
147 return ret;
149 if (cygheap->pg.nss_pwd_db ())
150 return cygheap->pg.pwd_cache.win.add_user_from_windows (name, pldap);
151 return NULL;
154 struct passwd *
155 internal_getpwuid (uid_t uid, cyg_ldap *pldap)
157 struct passwd *ret;
159 cygheap->pg.nss_init ();
160 /* Check caches first. */
161 if (cygheap->pg.nss_cygserver_caching ()
162 && (ret = cygheap->pg.pwd_cache.cygserver.find_user (uid)))
163 return ret;
164 if (cygheap->pg.nss_pwd_files ()
165 && (ret = cygheap->pg.pwd_cache.file.find_user (uid)))
166 return ret;
167 if (cygheap->pg.nss_pwd_db ()
168 && (ret = cygheap->pg.pwd_cache.win.find_user (uid)))
169 return ret;
170 /* Ask sources afterwards. */
171 if (cygheap->pg.nss_cygserver_caching ()
172 && (ret = cygheap->pg.pwd_cache.cygserver.add_user_from_cygserver (uid)))
173 return ret;
174 if (cygheap->pg.nss_pwd_files ())
176 cygheap->pg.pwd_cache.file.check_file ();
177 if ((ret = cygheap->pg.pwd_cache.file.add_user_from_file (uid)))
178 return ret;
180 if (cygheap->pg.nss_pwd_db () || uid == ILLEGAL_UID)
181 return cygheap->pg.pwd_cache.win.add_user_from_windows (uid, pldap);
182 return NULL;
185 /* getpwuid/getpwnam are not reentrant. */
186 static struct {
187 struct passwd p;
188 char *buf;
189 size_t bufsiz;
190 } app_pw;
192 static struct passwd *
193 getpw_cp (struct passwd *temppw)
195 if (!temppw)
196 return NULL;
197 pg_pwd *pw = (pg_pwd *) temppw;
198 if (app_pw.bufsiz < pw->len)
200 char *newbuf = (char *) realloc (app_pw.buf, pw->len);
201 if (!newbuf)
203 set_errno (ENOMEM);
204 return NULL;
206 app_pw.buf = newbuf;
207 app_pw.bufsiz = pw->len;
209 memcpy (app_pw.buf, pw->p.pw_name, pw->len);
210 memcpy (&app_pw.p, &pw->p, sizeof pw->p);
211 ptrdiff_t diff = app_pw.buf - pw->p.pw_name;
212 app_pw.p.pw_name += diff;
213 app_pw.p.pw_passwd += diff;
214 app_pw.p.pw_gecos += diff;
215 app_pw.p.pw_dir += diff;
216 app_pw.p.pw_shell += diff;
217 return &app_pw.p;
220 extern "C" struct passwd *
221 getpwuid (uid_t uid)
223 struct passwd *temppw = internal_getpwuid (uid);
224 pthread_testcancel ();
225 return getpw_cp (temppw);
228 extern "C" int
229 getpwuid_r (uid_t uid, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result)
231 *result = NULL;
233 if (!pwd || !buffer)
234 return ERANGE;
236 struct passwd *temppw = internal_getpwuid (uid);
237 pthread_testcancel ();
238 if (!temppw)
239 return 0;
241 /* check needed buffer size. */
242 size_t needsize = strlen (temppw->pw_name) + strlen (temppw->pw_passwd)
243 + strlen (temppw->pw_gecos) + strlen (temppw->pw_dir)
244 + strlen (temppw->pw_shell) + 5;
245 if (needsize > bufsize)
246 return ERANGE;
248 /* make a copy of temppw */
249 *result = pwd;
250 pwd->pw_uid = temppw->pw_uid;
251 pwd->pw_gid = temppw->pw_gid;
252 buffer = stpcpy (pwd->pw_name = buffer, temppw->pw_name);
253 buffer = stpcpy (pwd->pw_passwd = buffer + 1, temppw->pw_passwd);
254 buffer = stpcpy (pwd->pw_gecos = buffer + 1, temppw->pw_gecos);
255 buffer = stpcpy (pwd->pw_dir = buffer + 1, temppw->pw_dir);
256 stpcpy (pwd->pw_shell = buffer + 1, temppw->pw_shell);
257 pwd->pw_comment = NULL;
258 return 0;
261 extern "C" struct passwd *
262 getpwnam (const char *name)
264 struct passwd *temppw = internal_getpwnam (name);
265 pthread_testcancel ();
266 return getpw_cp (temppw);
270 /* the max size buffer we can expect to
271 * use is returned via sysconf with _SC_GETPW_R_SIZE_MAX.
272 * This may need updating! - Rob Collins April 2001.
274 extern "C" int
275 getpwnam_r (const char *nam, struct passwd *pwd, char *buffer, size_t bufsize, struct passwd **result)
277 *result = NULL;
279 if (!pwd || !buffer || !nam)
280 return ERANGE;
282 struct passwd *temppw = internal_getpwnam (nam);
283 pthread_testcancel ();
285 if (!temppw)
286 return 0;
288 /* check needed buffer size. */
289 size_t needsize = strlen (temppw->pw_name) + strlen (temppw->pw_passwd)
290 + strlen (temppw->pw_gecos) + strlen (temppw->pw_dir)
291 + strlen (temppw->pw_shell) + 5;
292 if (needsize > bufsize)
293 return ERANGE;
295 /* make a copy of temppw */
296 *result = pwd;
297 pwd->pw_uid = temppw->pw_uid;
298 pwd->pw_gid = temppw->pw_gid;
299 buffer = stpcpy (pwd->pw_name = buffer, temppw->pw_name);
300 buffer = stpcpy (pwd->pw_passwd = buffer + 1, temppw->pw_passwd);
301 buffer = stpcpy (pwd->pw_gecos = buffer + 1, temppw->pw_gecos);
302 buffer = stpcpy (pwd->pw_dir = buffer + 1, temppw->pw_dir);
303 stpcpy (pwd->pw_shell = buffer + 1, temppw->pw_shell);
304 pwd->pw_comment = NULL;
305 return 0;
308 /* getpwent functions are not reentrant. */
309 static pw_ent pwent;
311 void
312 pg_ent::clear_cache ()
314 if (pg.curr_lines)
316 if (state > from_file)
317 cfree (group ? grp.g.gr_name : pwd.p.pw_name);
318 pg.curr_lines = 0;
322 void
323 pg_ent::setent (bool _group, int _enums, PCWSTR _enum_tdoms)
325 cygheap->dom.init ();
326 endent (_group);
327 if (!_enums && !_enum_tdoms)
329 /* This is the default, when called from the usual setpwent/setgrent
330 functions. */
331 enums = cygheap->pg.nss_db_enums ();
332 enum_tdoms = cygheap->pg.nss_db_enum_tdoms ();
333 if (_group)
335 from_files = cygheap->pg.nss_grp_files ();
336 from_db = cygheap->pg.nss_grp_db ();
338 else
340 from_files = cygheap->pg.nss_pwd_files ();
341 from_db = cygheap->pg.nss_pwd_db ();
344 else
346 /* This case is when called from mkpasswd/mkgroup via cygwin_internal. */
347 enums = _enums;
348 enum_tdoms = _enum_tdoms;
349 from_files = false;
350 from_db = true;
352 state = from_cache;
355 void *
356 pg_ent::getent (void)
358 void *entry;
360 switch (state)
362 case rewound:
363 state = from_cache;
364 fallthrough;
365 case from_cache:
366 if (nss_db_enum_caches ()
367 && (entry = enumerate_caches ()))
368 return entry;
369 state = from_file;
370 fallthrough;
371 case from_file:
372 if (from_files
373 && nss_db_enum_files ()
374 && (entry = enumerate_file ()))
375 return entry;
376 state = from_builtin;
377 fallthrough;
378 case from_builtin:
379 if (from_db
380 && nss_db_enum_builtin ()
381 && (entry = enumerate_builtin ()))
382 return entry;
383 state = from_local;
384 fallthrough;
385 case from_local:
386 if (from_db
387 && nss_db_enum_local ()
388 /* Domain controller? If so, sam and ad are one and the same
389 and "local ad" would list all domain accounts twice without
390 this test. */
391 && (cygheap->dom.account_flat_name ()[0] != L'@'
392 || !nss_db_enum_primary ())
393 && (entry = enumerate_local ()))
394 return entry;
395 state = from_sam;
396 fallthrough;
397 case from_sam:
398 if (from_db
399 && nss_db_enum_local ()
400 /* Domain controller? If so, sam and ad are one and the same
401 and "local ad" would list all domain accounts twice without
402 this test. */
403 && (cygheap->dom.account_flat_name ()[0] != L'@'
404 || !nss_db_enum_primary ())
405 && (entry = enumerate_sam ()))
406 return entry;
407 state = from_ad;
408 fallthrough;
409 case from_ad:
410 if (cygheap->dom.member_machine ()
411 && from_db
412 && (entry = enumerate_ad ()))
413 return entry;
414 state = finished;
415 fallthrough;
416 case finished:
417 break;
419 return NULL;
422 void
423 pg_ent::endent (bool _group)
425 if (buf)
427 if (state == from_file)
428 free (buf);
429 else if (state == from_local || state == from_sam)
430 NetApiBufferFree (buf);
431 buf = NULL;
433 if (!pg.curr_lines)
435 if ((group = _group))
437 pg.init_grp ();
438 pg.pwdgrp_buf = (void *) &grp;
440 else
442 pg.init_pwd ();
443 pg.pwdgrp_buf = (void *) &pwd;
445 pg.max_lines = 1;
447 else
448 clear_cache ();
449 cldap.close ();
450 rl.close ();
451 cnt = max = resume = 0;
452 enums = 0;
453 enum_tdoms = NULL;
454 state = rewound;
457 void *
458 pg_ent::enumerate_file ()
460 void *entry;
462 if (!cnt)
464 pwdgrp &prf = group ? cygheap->pg.grp_cache.file
465 : cygheap->pg.pwd_cache.file;
466 if (prf.check_file ())
468 if (!buf)
469 buf = (char *) malloc (NT_MAX_PATH);
470 if (buf
471 && !rl.init (prf.file_attr (), buf, NT_MAX_PATH))
473 free (buf);
474 buf = NULL;
478 ++cnt;
479 if ((entry = pg.add_account_post_fetch (rl.gets (), false)))
480 return entry;
481 rl.close ();
482 free (buf);
483 buf = NULL;
484 cnt = max = resume = 0;
485 return NULL;
488 void *
489 pg_ent::enumerate_builtin ()
491 static cygpsid *pwd_builtins[] = {
492 &well_known_system_sid,
493 &well_known_local_service_sid,
494 &well_known_network_service_sid,
495 &well_known_admins_sid,
496 &trusted_installer_sid,
497 NULL
499 static cygpsid *grp_builtins[] = {
500 &well_known_system_sid,
501 &trusted_installer_sid,
502 NULL
505 cygpsid **builtins = group ? grp_builtins : pwd_builtins;
506 if (!builtins[cnt])
508 cnt = max = resume = 0;
509 return NULL;
511 cygsid sid (*builtins[cnt++]);
512 fetch_user_arg_t arg;
513 arg.type = SID_arg;
514 arg.sid = &sid;
515 char *line = pg.fetch_account_from_windows (arg);
516 return pg.add_account_post_fetch (line, false);
519 void *
520 pg_ent::enumerate_sam ()
522 while (true)
524 if (!cnt)
526 DWORD total;
527 NET_API_STATUS ret;
529 if (buf)
531 NetApiBufferFree (buf);
532 buf = NULL;
534 if (resume == ULONG_MAX)
535 ret = ERROR_NO_MORE_ITEMS;
536 else if (group)
537 ret = NetGroupEnum (NULL, 2, (PBYTE *) &buf, MAX_PREFERRED_LENGTH,
538 &max, &total, &resume);
539 else
540 ret = NetUserEnum (NULL, 20, FILTER_NORMAL_ACCOUNT, (PBYTE *) &buf,
541 MAX_PREFERRED_LENGTH, &max, &total,
542 (PDWORD) &resume);
543 if (ret == NERR_Success)
544 resume = ULONG_MAX;
545 else if (ret != ERROR_MORE_DATA)
547 cnt = max = resume = 0;
548 return NULL;
551 while (cnt < max)
553 cygsid sid (cygheap->dom.account_sid ());
554 sid_sub_auth (sid, sid_sub_auth_count (sid)) =
555 group ? ((PGROUP_INFO_2) buf)[cnt].grpi2_group_id
556 : ((PUSER_INFO_20) buf)[cnt].usri20_user_id;
557 ++cnt;
558 ++sid_sub_auth_count (sid);
559 fetch_user_arg_t arg;
560 arg.type = SID_arg;
561 arg.sid = &sid;
562 char *line = pg.fetch_account_from_windows (arg);
563 if (line)
564 return pg.add_account_post_fetch (line, false);
566 cnt = 0;
570 void *
571 pg_ent::enumerate_ad ()
573 while (true)
575 if (!cnt)
577 PDS_DOMAIN_TRUSTSW td;
579 if (!resume)
581 ++resume;
582 if (!nss_db_enum_primary ()
583 || cldap.enumerate_ad_accounts (NULL, group) != NO_ERROR)
584 continue;
585 RtlInitUnicodeString (&dom, cygheap->dom.primary_flat_name ());
587 else if ((td = cygheap->dom.trusted_domain (resume - 1)))
589 ++resume;
590 /* Ignore primary domain in list of trusted domains only if all
591 trusted domains are enumerated anyway. This handles an
592 annoying backward compatibility problem in mkpasswd/mkgroup.
593 Without this test, `mkpasswd -d PRIMARY_DOMAIN' wouldn't
594 work as expected. */
595 if (((enums & ENUM_TDOMS_ALL) && td->Flags & DS_DOMAIN_PRIMARY)
596 || !td->DomainSid
597 || (!nss_db_enum_tdom (td->NetbiosDomainName)
598 && !nss_db_enum_tdom (td->DnsDomainName))
599 || cldap.enumerate_ad_accounts (td->DnsDomainName, group)
600 != NO_ERROR)
601 continue;
602 RtlInitUnicodeString (&dom, td->NetbiosDomainName);
604 else
606 cldap.close ();
607 return NULL;
610 ++cnt;
611 cygsid sid;
612 int ret = cldap.next_account (sid);
613 if (ret == NO_ERROR)
615 fetch_acc_t full;
616 fetch_user_arg_t arg;
617 UNICODE_STRING name;
619 arg.type = FULL_acc_arg;
620 arg.full_acc = &full;
621 full.sid = sid;
622 RtlInitUnicodeString (&name,
623 cldap.get_string_attribute (L"sAMAccountName"));
624 full.name = &name;
625 full.dom = &dom;
626 if (sid_sub_auth (sid, 0) == SECURITY_BUILTIN_DOMAIN_RID)
627 full.acc_type = SidTypeAlias;
628 else
629 full.acc_type = group ? SidTypeGroup : SidTypeUser;
630 char *line = pg.fetch_account_from_windows (arg, &cldap);
631 if (line)
632 return pg.add_account_post_fetch (line, false);
633 ret = EIO;
635 if (ret != ENMFILE)
637 cldap.close ();
638 set_errno (ret);
639 return NULL;
641 cnt = 0;
645 void *
646 pw_ent::enumerate_caches ()
648 switch (max)
650 case 0:
651 if (cygheap->pg.nss_cygserver_caching ())
653 pwdgrp &prc = cygheap->pg.pwd_cache.cygserver;
654 if (cnt < prc.cached_users ())
655 return &prc.passwd ()[cnt++].p;
657 cnt = 0;
658 max = 1;
659 fallthrough;
660 case 1:
661 if (from_files)
663 pwdgrp &prf = cygheap->pg.pwd_cache.file;
664 prf.check_file ();
665 if (cnt < prf.cached_users ())
666 return &prf.passwd ()[cnt++].p;
668 cnt = 0;
669 max = 2;
670 fallthrough;
671 default:
672 if (from_db)
674 pwdgrp &prw = cygheap->pg.pwd_cache.win;
675 if (cnt < prw.cached_users ())
676 return &prw.passwd ()[cnt++].p;
678 break;
680 cnt = max = 0;
681 return NULL;
684 void *
685 pw_ent::enumerate_local ()
687 return NULL;
690 struct passwd *
691 pw_ent::getpwent (void)
693 if (state == rewound)
694 setent (false);
695 else
696 clear_cache ();
697 return (struct passwd *) getent ();
700 extern "C" void
701 setpwent ()
703 pwent.setpwent ();
706 extern "C" struct passwd *
707 getpwent (void)
709 return pwent.getpwent ();
712 extern "C" void
713 endpwent (void)
715 pwent.endpwent ();
718 /* *_filtered functions are called from mkpasswd */
719 void *
720 setpwent_filtered (int enums, PCWSTR enum_tdoms)
722 pw_ent *pw = new pw_ent;
723 if (pw)
724 pw->setpwent (enums, enum_tdoms);
725 return (void *) pw;
728 void *
729 getpwent_filtered (void *pw)
731 return (void *) ((pw_ent *) pw)->getpwent ();
734 void
735 endpwent_filtered (void *pw)
737 ((pw_ent *) pw)->endpwent ();
740 extern "C" int
741 setpassent (int)
743 return 0;
746 static void
747 _getpass_close_fd (void *arg)
749 if (arg)
750 fclose ((FILE *) arg);
753 extern "C" char *
754 getpass (const char * prompt)
756 char *pass = _my_tls.locals.pass;
757 struct termios ti, newti;
758 bool tc_set = false;
760 /* Try to use controlling tty in the first place. Use stdin and stderr
761 only as fallback. */
762 FILE *in = stdin, *err = stderr;
763 FILE *tty = fopen ("/dev/tty", "w+b");
764 pthread_cleanup_push (_getpass_close_fd, tty);
765 if (tty)
767 /* Set close-on-exec for obvious reasons. */
768 fcntl (fileno (tty), F_SETFD, fcntl (fileno (tty), F_GETFD) | FD_CLOEXEC);
769 in = err = tty;
772 /* Make sure to notice if stdin is closed. */
773 if (fileno (in) >= 0)
775 flockfile (in);
776 /* Change tty attributes if possible. */
777 if (!tcgetattr (fileno (in), &ti))
779 newti = ti;
780 newti.c_lflag &= ~(ECHO | ISIG); /* No echo, no signal handling. */
781 if (!tcsetattr (fileno (in), TCSANOW, &newti))
782 tc_set = true;
784 fputs (prompt, err);
785 fflush (err);
786 fgets (pass, _PASSWORD_LEN, in);
787 fprintf (err, "\n");
788 if (tc_set)
789 tcsetattr (fileno (in), TCSANOW, &ti);
790 funlockfile (in);
791 char *crlf = strpbrk (pass, "\r\n");
792 if (crlf)
793 *crlf = '\0';
795 pthread_cleanup_pop (1);
796 return pass;