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
20 #include "shared_info.h"
21 #include "miscfuncs.h"
25 /* Parse /etc/passwd line into passwd structure. */
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
))
34 if (!next_num (res
.p
.pw_gid
))
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 (':');
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;
52 pwdgrp_buf_elem_size
= sizeof (pg_pwd
);
53 parse
= &pwdgrp::parse_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
;
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
;
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
;
85 internal_getpwsid (cygpsid
&sid
, cyg_ldap
*pldap
)
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
)))
94 if (cygheap
->pg
.nss_pwd_files ()
95 && (ret
= cygheap
->pg
.pwd_cache
.file
.find_user (sid
)))
97 if (cygheap
->pg
.nss_pwd_db ()
98 && (ret
= cygheap
->pg
.pwd_cache
.win
.find_user (sid
)))
100 /* Ask sources afterwards. */
101 if (cygheap
->pg
.nss_cygserver_caching ()
102 && (ret
= cygheap
->pg
.pwd_cache
.cygserver
.add_user_from_cygserver (sid
)))
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
)))
110 if (cygheap
->pg
.nss_pwd_db ())
111 return cygheap
->pg
.pwd_cache
.win
.add_user_from_windows (sid
, pldap
);
115 /* This function gets only called from mkpasswd via cygwin_internal. */
117 internal_getpwsid_from_db (cygpsid
&sid
)
119 cygheap
->pg
.nss_init ();
120 return cygheap
->pg
.pwd_cache
.win
.add_user_from_windows (sid
);
124 internal_getpwnam (const char *name
, cyg_ldap
*pldap
)
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
)))
133 if (cygheap
->pg
.nss_pwd_files ()
134 && (ret
= cygheap
->pg
.pwd_cache
.file
.find_user (name
)))
136 if (cygheap
->pg
.nss_pwd_db ()
137 && (ret
= cygheap
->pg
.pwd_cache
.win
.find_user (name
)))
139 /* Ask sources afterwards. */
140 if (cygheap
->pg
.nss_cygserver_caching ()
141 && (ret
= cygheap
->pg
.pwd_cache
.cygserver
.add_user_from_cygserver (name
)))
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
)))
149 if (cygheap
->pg
.nss_pwd_db ())
150 return cygheap
->pg
.pwd_cache
.win
.add_user_from_windows (name
, pldap
);
155 internal_getpwuid (uid_t uid
, cyg_ldap
*pldap
)
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
)))
164 if (cygheap
->pg
.nss_pwd_files ()
165 && (ret
= cygheap
->pg
.pwd_cache
.file
.find_user (uid
)))
167 if (cygheap
->pg
.nss_pwd_db ()
168 && (ret
= cygheap
->pg
.pwd_cache
.win
.find_user (uid
)))
170 /* Ask sources afterwards. */
171 if (cygheap
->pg
.nss_cygserver_caching ()
172 && (ret
= cygheap
->pg
.pwd_cache
.cygserver
.add_user_from_cygserver (uid
)))
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
)))
180 if (cygheap
->pg
.nss_pwd_db () || uid
== ILLEGAL_UID
)
181 return cygheap
->pg
.pwd_cache
.win
.add_user_from_windows (uid
, pldap
);
185 /* getpwuid/getpwnam are not reentrant. */
192 static struct passwd
*
193 getpw_cp (struct passwd
*temppw
)
197 pg_pwd
*pw
= (pg_pwd
*) temppw
;
198 if (app_pw
.bufsiz
< pw
->len
)
200 char *newbuf
= (char *) realloc (app_pw
.buf
, pw
->len
);
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
;
220 extern "C" struct passwd
*
223 struct passwd
*temppw
= internal_getpwuid (uid
);
224 pthread_testcancel ();
225 return getpw_cp (temppw
);
229 getpwuid_r (uid_t uid
, struct passwd
*pwd
, char *buffer
, size_t bufsize
, struct passwd
**result
)
236 struct passwd
*temppw
= internal_getpwuid (uid
);
237 pthread_testcancel ();
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
)
248 /* make a copy of temppw */
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
;
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.
275 getpwnam_r (const char *nam
, struct passwd
*pwd
, char *buffer
, size_t bufsize
, struct passwd
**result
)
279 if (!pwd
|| !buffer
|| !nam
)
282 struct passwd
*temppw
= internal_getpwnam (nam
);
283 pthread_testcancel ();
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
)
295 /* make a copy of temppw */
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
;
308 /* getpwent functions are not reentrant. */
312 pg_ent::clear_cache ()
316 if (state
> from_file
)
317 cfree (group
? grp
.g
.gr_name
: pwd
.p
.pw_name
);
323 pg_ent::setent (bool _group
, int _enums
, PCWSTR _enum_tdoms
)
325 cygheap
->dom
.init ();
327 if (!_enums
&& !_enum_tdoms
)
329 /* This is the default, when called from the usual setpwent/setgrent
331 enums
= cygheap
->pg
.nss_db_enums ();
332 enum_tdoms
= cygheap
->pg
.nss_db_enum_tdoms ();
335 from_files
= cygheap
->pg
.nss_grp_files ();
336 from_db
= cygheap
->pg
.nss_grp_db ();
340 from_files
= cygheap
->pg
.nss_pwd_files ();
341 from_db
= cygheap
->pg
.nss_pwd_db ();
346 /* This case is when called from mkpasswd/mkgroup via cygwin_internal. */
348 enum_tdoms
= _enum_tdoms
;
356 pg_ent::getent (void)
366 if (nss_db_enum_caches ()
367 && (entry
= enumerate_caches ()))
373 && nss_db_enum_files ()
374 && (entry
= enumerate_file ()))
376 state
= from_builtin
;
380 && nss_db_enum_builtin ()
381 && (entry
= enumerate_builtin ()))
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
391 && (cygheap
->dom
.account_flat_name ()[0] != L
'@'
392 || !nss_db_enum_primary ())
393 && (entry
= enumerate_local ()))
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
403 && (cygheap
->dom
.account_flat_name ()[0] != L
'@'
404 || !nss_db_enum_primary ())
405 && (entry
= enumerate_sam ()))
410 if (cygheap
->dom
.member_machine ()
412 && (entry
= enumerate_ad ()))
423 pg_ent::endent (bool _group
)
427 if (state
== from_file
)
429 else if (state
== from_local
|| state
== from_sam
)
430 NetApiBufferFree (buf
);
435 if ((group
= _group
))
438 pg
.pwdgrp_buf
= (void *) &grp
;
443 pg
.pwdgrp_buf
= (void *) &pwd
;
451 cnt
= max
= resume
= 0;
458 pg_ent::enumerate_file ()
464 pwdgrp
&prf
= group
? cygheap
->pg
.grp_cache
.file
465 : cygheap
->pg
.pwd_cache
.file
;
466 if (prf
.check_file ())
469 buf
= (char *) malloc (NT_MAX_PATH
);
471 && !rl
.init (prf
.file_attr (), buf
, NT_MAX_PATH
))
479 if ((entry
= pg
.add_account_post_fetch (rl
.gets (), false)))
484 cnt
= max
= resume
= 0;
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
,
499 static cygpsid
*grp_builtins
[] = {
500 &well_known_system_sid
,
501 &trusted_installer_sid
,
505 cygpsid
**builtins
= group
? grp_builtins
: pwd_builtins
;
508 cnt
= max
= resume
= 0;
511 cygsid
sid (*builtins
[cnt
++]);
512 fetch_user_arg_t arg
;
515 char *line
= pg
.fetch_account_from_windows (arg
);
516 return pg
.add_account_post_fetch (line
, false);
520 pg_ent::enumerate_sam ()
531 NetApiBufferFree (buf
);
534 if (resume
== ULONG_MAX
)
535 ret
= ERROR_NO_MORE_ITEMS
;
537 ret
= NetGroupEnum (NULL
, 2, (PBYTE
*) &buf
, MAX_PREFERRED_LENGTH
,
538 &max
, &total
, &resume
);
540 ret
= NetUserEnum (NULL
, 20, FILTER_NORMAL_ACCOUNT
, (PBYTE
*) &buf
,
541 MAX_PREFERRED_LENGTH
, &max
, &total
,
543 if (ret
== NERR_Success
)
545 else if (ret
!= ERROR_MORE_DATA
)
547 cnt
= max
= resume
= 0;
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
;
558 ++sid_sub_auth_count (sid
);
559 fetch_user_arg_t arg
;
562 char *line
= pg
.fetch_account_from_windows (arg
);
564 return pg
.add_account_post_fetch (line
, false);
571 pg_ent::enumerate_ad ()
577 PDS_DOMAIN_TRUSTSW td
;
582 if (!nss_db_enum_primary ()
583 || cldap
.enumerate_ad_accounts (NULL
, group
) != NO_ERROR
)
585 RtlInitUnicodeString (&dom
, cygheap
->dom
.primary_flat_name ());
587 else if ((td
= cygheap
->dom
.trusted_domain (resume
- 1)))
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
595 if (((enums
& ENUM_TDOMS_ALL
) && td
->Flags
& DS_DOMAIN_PRIMARY
)
597 || (!nss_db_enum_tdom (td
->NetbiosDomainName
)
598 && !nss_db_enum_tdom (td
->DnsDomainName
))
599 || cldap
.enumerate_ad_accounts (td
->DnsDomainName
, group
)
602 RtlInitUnicodeString (&dom
, td
->NetbiosDomainName
);
612 int ret
= cldap
.next_account (sid
);
616 fetch_user_arg_t arg
;
619 arg
.type
= FULL_acc_arg
;
620 arg
.full_acc
= &full
;
622 RtlInitUnicodeString (&name
,
623 cldap
.get_string_attribute (L
"sAMAccountName"));
626 if (sid_sub_auth (sid
, 0) == SECURITY_BUILTIN_DOMAIN_RID
)
627 full
.acc_type
= SidTypeAlias
;
629 full
.acc_type
= group
? SidTypeGroup
: SidTypeUser
;
630 char *line
= pg
.fetch_account_from_windows (arg
, &cldap
);
632 return pg
.add_account_post_fetch (line
, false);
646 pw_ent::enumerate_caches ()
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
;
663 pwdgrp
&prf
= cygheap
->pg
.pwd_cache
.file
;
665 if (cnt
< prf
.cached_users ())
666 return &prf
.passwd ()[cnt
++].p
;
674 pwdgrp
&prw
= cygheap
->pg
.pwd_cache
.win
;
675 if (cnt
< prw
.cached_users ())
676 return &prw
.passwd ()[cnt
++].p
;
685 pw_ent::enumerate_local ()
691 pw_ent::getpwent (void)
693 if (state
== rewound
)
697 return (struct passwd
*) getent ();
706 extern "C" struct passwd
*
709 return pwent
.getpwent ();
718 /* *_filtered functions are called from mkpasswd */
720 setpwent_filtered (int enums
, PCWSTR enum_tdoms
)
722 pw_ent
*pw
= new pw_ent
;
724 pw
->setpwent (enums
, enum_tdoms
);
729 getpwent_filtered (void *pw
)
731 return (void *) ((pw_ent
*) pw
)->getpwent ();
735 endpwent_filtered (void *pw
)
737 ((pw_ent
*) pw
)->endpwent ();
747 _getpass_close_fd (void *arg
)
750 fclose ((FILE *) arg
);
754 getpass (const char * prompt
)
756 char *pass
= _my_tls
.locals
.pass
;
757 struct termios ti
, newti
;
760 /* Try to use controlling tty in the first place. Use stdin and stderr
762 FILE *in
= stdin
, *err
= stderr
;
763 FILE *tty
= fopen ("/dev/tty", "w+b");
764 pthread_cleanup_push (_getpass_close_fd
, tty
);
767 /* Set close-on-exec for obvious reasons. */
768 fcntl (fileno (tty
), F_SETFD
, fcntl (fileno (tty
), F_GETFD
) | FD_CLOEXEC
);
772 /* Make sure to notice if stdin is closed. */
773 if (fileno (in
) >= 0)
776 /* Change tty attributes if possible. */
777 if (!tcgetattr (fileno (in
), &ti
))
780 newti
.c_lflag
&= ~(ECHO
| ISIG
); /* No echo, no signal handling. */
781 if (!tcsetattr (fileno (in
), TCSANOW
, &newti
))
786 fgets (pass
, _PASSWORD_LEN
, in
);
789 tcsetattr (fileno (in
), TCSANOW
, &ti
);
791 char *crlf
= strpbrk (pass
, "\r\n");
795 pthread_cleanup_pop (1);