1 /* Copyright (C) 1996, 1997 Free Software Foundation, Inc.
2 This file is part of the GNU C Library.
3 Contributed by Thorsten Kukuk <kukuk@vt.uni-paderborn.de>, 1996.
5 The GNU C Library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public License as
7 published by the Free Software Foundation; either version 2 of the
8 License, or (at your option) any later version.
10 The GNU C Library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Library General Public License for more details.
15 You should have received a copy of the GNU Library General Public
16 License along with the GNU C Library; see the file COPYING.LIB. If not,
17 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
18 Boston, MA 02111-1307, USA. */
26 #include <libc-lock.h>
27 #include <rpcsvc/yp.h>
28 #include <rpcsvc/ypclnt.h>
29 #include <rpcsvc/nis.h>
30 #include <rpcsvc/nislib.h>
34 #include "nss-nisplus.h"
36 static service_user
*ni
= NULL
;
37 static bool_t use_nisplus
= FALSE
; /* default: passwd_compat: nis */
39 /* Get the declaration of the parser function. */
41 #define STRUCTURE spwd
43 #include "../../nss/nss_files/files-parse.c"
45 /* Structure for remembering -@netgroup and -user members ... */
46 #define BLACKLIST_INITIAL_SIZE 512
47 #define BLACKLIST_INCREMENT 256
66 struct blacklist_t blacklist
;
68 struct __netgrent netgrdata
;
70 typedef struct ent_t ent_t
;
72 static ent_t ext_ent
= {0, 0, 0, NULL
, 0, NULL
, NULL
, 0, NULL
, {NULL
, 0, 0},
73 {NULL
, NULL
, 0, 0, 0, 0, 0, 0, 0}};
75 /* Protect global state against multiple changers. */
76 __libc_lock_define_initialized (static, lock
)
78 /* Prototypes for local functions. */
79 static void blacklist_store_name (const char *, ent_t
*);
80 static int in_blacklist (const char *, int, ent_t
*);
81 extern int _nss_nisplus_parse_spent (nis_result
*, struct spwd
*,
84 give_spwd_free (struct spwd
*pwd
)
86 if (pwd
->sp_namp
!= NULL
)
88 if (pwd
->sp_pwdp
!= NULL
)
91 memset (pwd
, '\0', sizeof (struct spwd
));
95 spwd_need_buflen (struct spwd
*pwd
)
99 if (pwd
->sp_pwdp
!= NULL
)
100 len
+= strlen (pwd
->sp_pwdp
) + 1;
106 copy_spwd_changes (struct spwd
*dest
, struct spwd
*src
,
107 char *buffer
, size_t buflen
)
109 if (src
->sp_pwdp
!= NULL
&& strlen (src
->sp_pwdp
))
112 dest
->sp_pwdp
= strdup (src
->sp_pwdp
);
113 else if (dest
->sp_pwdp
&&
114 strlen (dest
->sp_pwdp
) >= strlen (src
->sp_pwdp
))
115 strcpy (dest
->sp_pwdp
, src
->sp_pwdp
);
118 dest
->sp_pwdp
= buffer
;
119 strcpy (dest
->sp_pwdp
, src
->sp_pwdp
);
120 buffer
+= strlen (dest
->sp_pwdp
) + 1;
121 buflen
= buflen
- (strlen (dest
->sp_pwdp
) + 1);
124 if (src
->sp_lstchg
!= 0)
125 dest
->sp_lstchg
= src
->sp_lstchg
;
126 if (src
->sp_min
!= 0)
127 dest
->sp_min
= src
->sp_min
;
128 if (src
->sp_max
!= 0)
129 dest
->sp_max
= src
->sp_max
;
130 if (src
->sp_warn
!= 0)
131 dest
->sp_warn
= src
->sp_warn
;
132 if (src
->sp_inact
!= 0)
133 dest
->sp_inact
= src
->sp_inact
;
134 if (src
->sp_expire
!= 0)
135 dest
->sp_expire
= src
->sp_expire
;
136 if (src
->sp_flag
!= 0)
137 dest
->sp_flag
= src
->sp_flag
;
140 static enum nss_status
141 internal_setspent (ent_t
*ent
)
143 enum nss_status status
= NSS_STATUS_SUCCESS
;
145 ent
->nis
= ent
->first
= ent
->netgroup
= 0;
147 /* If something was left over free it. */
149 __internal_endnetgrent (&ent
->netgrdata
);
151 if (ent
->oldkey
!= NULL
)
158 if (ent
->result
!= NULL
)
160 nis_freeresult (ent
->result
);
163 if (ent
->names
!= NULL
)
165 nis_freenames (ent
->names
);
170 ent
->blacklist
.current
= 0;
171 if (ent
->blacklist
.data
!= NULL
)
172 ent
->blacklist
.data
[0] = '\0';
174 if (ent
->stream
== NULL
)
176 ent
->stream
= fopen ("/etc/shadow", "r");
178 if (ent
->stream
== NULL
)
179 status
= errno
== EAGAIN
? NSS_STATUS_TRYAGAIN
: NSS_STATUS_UNAVAIL
;
182 rewind (ent
->stream
);
184 give_spwd_free (&ent
->pwd
);
191 _nss_compat_setspent (void)
193 enum nss_status result
;
195 __libc_lock_lock (lock
);
199 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni
);
200 use_nisplus
= (strcmp (ni
->name
, "nisplus") == 0);
203 result
= internal_setspent (&ext_ent
);
205 __libc_lock_unlock (lock
);
211 static enum nss_status
212 internal_endspent (ent_t
*ent
)
214 if (ent
->stream
!= NULL
)
216 fclose (ent
->stream
);
221 __internal_endnetgrent (&ent
->netgrdata
);
223 ent
->nis
= ent
->first
= ent
->netgroup
= 0;
225 if (ent
->oldkey
!= NULL
)
232 if (ent
->result
!= NULL
)
234 nis_freeresult (ent
->result
);
237 if (ent
->names
!= NULL
)
239 nis_freenames (ent
->names
);
244 ent
->blacklist
.current
= 0;
245 if (ent
->blacklist
.data
!= NULL
)
246 ent
->blacklist
.data
[0] = '\0';
248 give_spwd_free (&ent
->pwd
);
250 return NSS_STATUS_SUCCESS
;
254 _nss_compat_endspent (void)
256 enum nss_status result
;
258 __libc_lock_lock (lock
);
260 result
= internal_endspent (&ext_ent
);
262 __libc_lock_unlock (lock
);
268 static enum nss_status
269 getspent_next_nis_netgr (struct spwd
*result
, ent_t
*ent
, char *group
,
270 char *buffer
, size_t buflen
)
272 struct parser_data
*data
= (void *) buffer
;
273 char *ypdomain
, *host
, *user
, *domain
, *outval
, *p
, *p2
;
274 int status
, outvallen
;
277 if (yp_get_default_domain (&ypdomain
) != YPERR_SUCCESS
)
281 give_spwd_free (&ent
->pwd
);
282 return NSS_STATUS_UNAVAIL
;
285 if (ent
->first
== TRUE
)
287 bzero (&ent
->netgrdata
, sizeof (struct __netgrent
));
288 __internal_setnetgrent (group
, &ent
->netgrdata
);
294 status
= __internal_getnetgrent_r (&host
, &user
, &domain
,
295 &ent
->netgrdata
, buffer
, buflen
);
298 __internal_endnetgrent (&ent
->netgrdata
);
300 give_spwd_free (&ent
->pwd
);
301 return NSS_STATUS_RETURN
;
304 if (user
== NULL
|| user
[0] == '-')
307 if (domain
!= NULL
&& strcmp (ypdomain
, domain
) != 0)
310 if (yp_match (ypdomain
, "shadow.byname", user
,
311 strlen (user
), &outval
, &outvallen
)
315 p2len
= spwd_need_buflen (&ent
->pwd
);
318 __set_errno (ERANGE
);
319 return NSS_STATUS_TRYAGAIN
;
321 p2
= buffer
+ (buflen
- p2len
);
323 p
= strncpy (buffer
, outval
, buflen
);
327 if (_nss_files_parse_spent (p
, result
, data
, buflen
))
329 copy_spwd_changes (result
, &ent
->pwd
, p2
, p2len
);
334 return NSS_STATUS_SUCCESS
;
337 static enum nss_status
338 getspent_next_nisplus_netgr (struct spwd
*result
, ent_t
*ent
, char *group
,
339 char *buffer
, size_t buflen
)
341 char *ypdomain
, *host
, *user
, *domain
, *p2
;
342 int status
, parse_res
;
346 /* Maybe we should use domainname here ? We need the current
347 domainname for the domain field in netgroups */
348 if (yp_get_default_domain (&ypdomain
) != YPERR_SUCCESS
)
352 give_spwd_free (&ent
->pwd
);
353 return NSS_STATUS_UNAVAIL
;
356 if (ent
->first
== TRUE
)
358 bzero (&ent
->netgrdata
, sizeof (struct __netgrent
));
359 __internal_setnetgrent (group
, &ent
->netgrdata
);
365 status
= __internal_getnetgrent_r (&host
, &user
, &domain
,
366 &ent
->netgrdata
, buffer
, buflen
);
369 __internal_endnetgrent (&ent
->netgrdata
);
371 give_spwd_free (&ent
->pwd
);
372 return NSS_STATUS_RETURN
;
375 if (user
== NULL
|| user
[0] == '-')
378 if (domain
!= NULL
&& strcmp (ypdomain
, domain
) != 0)
381 p2len
= spwd_need_buflen (&ent
->pwd
);
384 __set_errno (ERANGE
);
385 return NSS_STATUS_TRYAGAIN
;
387 p2
= buffer
+ (buflen
- p2len
);
390 char buf
[strlen (user
) + 30];
391 sprintf(buf
, "[name=%s],passwd.org_dir", user
);
392 nisres
= nis_list(buf
, EXPAND_NAME
, NULL
, NULL
);
394 if (niserr2nss (nisres
->status
) != NSS_STATUS_SUCCESS
)
396 nis_freeresult (nisres
);
399 parse_res
= _nss_nisplus_parse_spent (nisres
, result
, buffer
, buflen
);
400 nis_freeresult (nisres
);
404 copy_spwd_changes (result
, &ent
->pwd
, p2
, p2len
);
409 return NSS_STATUS_SUCCESS
;
412 static enum nss_status
413 getspent_next_netgr (struct spwd
*result
, ent_t
*ent
, char *group
,
414 char *buffer
, size_t buflen
)
417 return getspent_next_nisplus_netgr (result
, ent
, group
, buffer
, buflen
);
419 return getspent_next_nis_netgr (result
, ent
, group
, buffer
, buflen
);
422 static enum nss_status
423 getspent_next_nisplus (struct spwd
*result
, ent_t
*ent
, char *buffer
,
430 if (ent
->names
== NULL
)
432 ent
->names
= nis_getnames ("passwd.org_dir");
433 if (ent
->names
== NULL
|| ent
->names
[0] == NULL
)
436 return NSS_STATUS_UNAVAIL
;
440 p2len
= spwd_need_buflen (&ent
->pwd
);
443 __set_errno (ERANGE
);
444 return NSS_STATUS_TRYAGAIN
;
446 p2
= buffer
+ (buflen
- p2len
);
453 ent
->result
= nis_first_entry(ent
->names
[ent
->names_nr
]);
454 if (niserr2nss (ent
->result
->status
) != NSS_STATUS_SUCCESS
)
457 give_spwd_free (&ent
->pwd
);
458 return niserr2nss (ent
->result
->status
);
466 res
= nis_next_entry(ent
->names
[ent
->names_nr
],
467 &ent
->result
->cookie
);
468 nis_freeresult (ent
->result
);
470 if (niserr2nss (ent
->result
->status
) != NSS_STATUS_SUCCESS
)
472 if ((ent
->result
->status
== NIS_NOTFOUND
) &&
473 ent
->names
[ent
->names_nr
+ 1] != NULL
)
475 nis_freeresult (ent
->result
);
482 give_spwd_free (&ent
->pwd
);
483 return niserr2nss (ent
->result
->status
);
487 parse_res
= _nss_nisplus_parse_spent (ent
->result
, result
, buffer
,
490 in_blacklist (result
->sp_namp
, strlen (result
->sp_namp
), ent
))
491 parse_res
= 0; /* if result->pw_name in blacklist,search next entry */
495 copy_spwd_changes (result
, &ent
->pwd
, p2
, p2len
);
497 return NSS_STATUS_SUCCESS
;
501 static enum nss_status
502 getspent_next_nis (struct spwd
*result
, ent_t
*ent
,
503 char *buffer
, size_t buflen
)
505 struct parser_data
*data
= (void *) buffer
;
506 char *domain
, *outkey
, *outval
, *p
, *p2
;
507 int outkeylen
, outvallen
, parse_res
;
510 if (yp_get_default_domain (&domain
) != YPERR_SUCCESS
)
513 give_spwd_free (&ent
->pwd
);
514 return NSS_STATUS_UNAVAIL
;
517 p2len
= spwd_need_buflen (&ent
->pwd
);
520 __set_errno (ERANGE
);
521 return NSS_STATUS_TRYAGAIN
;
523 p2
= buffer
+ (buflen
- p2len
);
529 if (yp_first (domain
, "shadow.byname", &outkey
, &outkeylen
,
530 &outval
, &outvallen
) != YPERR_SUCCESS
)
533 give_spwd_free (&ent
->pwd
);
534 return NSS_STATUS_UNAVAIL
;
537 ent
->oldkey
= outkey
;
538 ent
->oldkeylen
= outkeylen
;
543 if (yp_next (domain
, "shadow.byname", ent
->oldkey
, ent
->oldkeylen
,
544 &outkey
, &outkeylen
, &outval
, &outvallen
)
548 give_spwd_free (&ent
->pwd
);
549 return NSS_STATUS_NOTFOUND
;
553 ent
->oldkey
= outkey
;
554 ent
->oldkeylen
= outkeylen
;
557 /* Copy the found data to our buffer */
558 p
= strncpy (buffer
, outval
, buflen
);
560 /* ...and free the data. */
565 parse_res
= _nss_files_parse_spent (p
, result
, data
, buflen
);
567 in_blacklist (result
->sp_namp
, strlen (result
->sp_namp
), ent
))
572 copy_spwd_changes (result
, &ent
->pwd
, p2
, p2len
);
574 return NSS_STATUS_SUCCESS
;
577 /* This function handle the +user entrys in /etc/shadow */
578 static enum nss_status
579 getspent_next_file_plususer (struct spwd
*result
, char *buffer
,
582 struct parser_data
*data
= (void *) buffer
;
588 memset (&pwd
, '\0', sizeof (struct spwd
));
590 copy_spwd_changes (&pwd
, result
, NULL
, 0);
592 plen
= spwd_need_buflen (&pwd
);
595 __set_errno (ERANGE
);
596 return NSS_STATUS_TRYAGAIN
;
598 p
= buffer
+ (buflen
- plen
);
601 if (use_nisplus
) /* Do the NIS+ query here */
604 char buf
[strlen (result
->sp_namp
) + 24];
606 sprintf(buf
, "[name=%s],passwd.org_dir",
607 &result
->sp_namp
[1]);
608 res
= nis_list(buf
, EXPAND_NAME
, NULL
, NULL
);
609 if (niserr2nss (res
->status
) != NSS_STATUS_SUCCESS
)
611 enum nss_status status
= niserr2nss (res
->status
);
613 nis_freeresult (res
);
616 parse_res
= _nss_nisplus_parse_spent (res
, result
, buffer
, buflen
);
617 nis_freeresult (res
);
625 if (yp_get_default_domain (&domain
) != YPERR_SUCCESS
)
626 return NSS_STATUS_TRYAGAIN
;
628 if (yp_match (domain
, "passwd.byname", &result
->sp_namp
[1],
629 strlen (result
->sp_namp
) - 1, &outval
, &outvallen
)
631 return NSS_STATUS_TRYAGAIN
;
632 p
= strncpy (buffer
, outval
,
633 buflen
< (size_t) outvallen
? buflen
: (size_t) outvallen
);
637 parse_res
= _nss_files_parse_spent (p
, result
, data
, buflen
);
642 copy_spwd_changes (result
, &pwd
, p
, plen
);
643 give_spwd_free (&pwd
);
644 /* We found the entry. */
645 return NSS_STATUS_SUCCESS
;
649 /* Give buffer the old len back */
651 give_spwd_free (&pwd
);
653 return NSS_STATUS_RETURN
;
656 static enum nss_status
657 getspent_next_file (struct spwd
*result
, ent_t
*ent
,
658 char *buffer
, size_t buflen
)
660 struct parser_data
*data
= (void *) buffer
;
667 p
= fgets (buffer
, buflen
, ent
->stream
);
669 return NSS_STATUS_NOTFOUND
;
671 /* Terminate the line for any case. */
672 buffer
[buflen
- 1] = '\0';
674 /* Skip leading blanks. */
678 while (*p
== '\0' || *p
== '#' /* Ignore empty and comment lines. */
679 /* Parse the line. If it is invalid, loop to
680 get the next line of the file to parse. */
681 || !_nss_files_parse_spent (p
, result
, data
, buflen
));
683 if (result
->sp_namp
[0] != '+' && result
->sp_namp
[0] != '-')
684 /* This is a real entry. */
688 if (result
->sp_namp
[0] == '-' && result
->sp_namp
[1] == '@'
689 && result
->sp_namp
[2] != '\0')
692 char *user
, *host
, *domain
;
693 struct __netgrent netgrdata
;
695 bzero (&netgrdata
, sizeof (struct __netgrent
));
696 __internal_setnetgrent (&result
->sp_namp
[2], &netgrdata
);
697 while (__internal_getnetgrent_r (&host
, &user
, &domain
,
698 &netgrdata
, buf2
, sizeof (buf2
)))
700 if (user
!= NULL
&& user
[0] != '-')
701 blacklist_store_name (user
, ent
);
703 __internal_endnetgrent (&netgrdata
);
708 if (result
->sp_namp
[0] == '+' && result
->sp_namp
[1] == '@'
709 && result
->sp_namp
[2] != '\0')
713 ent
->netgroup
= TRUE
;
715 copy_spwd_changes (&ent
->pwd
, result
, NULL
, 0);
717 status
= getspent_next_netgr (result
, ent
, &result
->sp_namp
[2],
719 if (status
== NSS_STATUS_RETURN
)
726 if (result
->sp_namp
[0] == '-' && result
->sp_namp
[1] != '\0'
727 && result
->sp_namp
[1] != '@')
729 blacklist_store_name (&result
->sp_namp
[1], ent
);
734 if (result
->sp_namp
[0] == '+' && result
->sp_namp
[1] != '\0'
735 && result
->sp_namp
[1] != '@')
737 enum nss_status status
;
739 status
= getspent_next_file_plususer (result
, buffer
, buflen
);
740 if (status
== NSS_STATUS_SUCCESS
) /* We found the entry. */
743 if (status
== NSS_STATUS_RETURN
) /* We couldn't parse the entry */
750 if (result
->sp_namp
[0] == '+' && result
->sp_namp
[1] == '\0')
754 copy_spwd_changes (&ent
->pwd
, result
, NULL
, 0);
757 return getspent_next_nisplus (result
, ent
, buffer
, buflen
);
759 return getspent_next_nis (result
, ent
, buffer
, buflen
);
763 return NSS_STATUS_SUCCESS
;
767 static enum nss_status
768 internal_getspent_r (struct spwd
*pw
, ent_t
*ent
,
769 char *buffer
, size_t buflen
)
775 /* We are searching members in a netgroup */
776 /* Since this is not the first call, we don't need the group name */
777 status
= getspent_next_netgr (pw
, ent
, NULL
, buffer
, buflen
);
778 if (status
== NSS_STATUS_RETURN
)
779 return getspent_next_file (pw
, ent
, buffer
, buflen
);
786 return getspent_next_nisplus (pw
, ent
, buffer
, buflen
);
788 return getspent_next_nis (pw
, ent
, buffer
, buflen
);
791 return getspent_next_file (pw
, ent
, buffer
, buflen
);
795 _nss_compat_getspent_r (struct spwd
*pwd
, char *buffer
, size_t buflen
)
797 enum nss_status status
= NSS_STATUS_SUCCESS
;
799 __libc_lock_lock (lock
);
803 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni
);
804 use_nisplus
= (strcmp (ni
->name
, "nisplus") == 0);
807 /* Be prepared that the setspent function was not called before. */
808 if (ext_ent
.stream
== NULL
)
809 status
= internal_setspent (&ext_ent
);
811 if (status
== NSS_STATUS_SUCCESS
)
812 status
= internal_getspent_r (pwd
, &ext_ent
, buffer
, buflen
);
814 __libc_lock_unlock (lock
);
821 _nss_compat_getspnam_r (const char *name
, struct spwd
*pwd
,
822 char *buffer
, size_t buflen
)
824 ent_t ent
= {0, 0, 0, NULL
, 0, NULL
, NULL
, 0, NULL
, {NULL
, 0, 0},
825 {NULL
, NULL
, 0, 0, 0, 0, 0, 0, 0}};
826 enum nss_status status
;
828 if (name
[0] == '-' || name
[0] == '+')
829 return NSS_STATUS_NOTFOUND
;
833 __nss_database_lookup ("shadow_compat", "passwd_compat", "nis", &ni
);
834 use_nisplus
= (strcmp (ni
->name
, "nisplus") == 0);
837 status
= internal_setspent (&ent
);
838 if (status
!= NSS_STATUS_SUCCESS
)
841 while ((status
= internal_getspent_r (pwd
, &ent
, buffer
, buflen
))
842 == NSS_STATUS_SUCCESS
)
843 if (strcmp (pwd
->sp_namp
, name
) == 0)
846 internal_endspent (&ent
);
850 /* Support routines for remembering -@netgroup and -user entries.
851 The names are stored in a single string with `|' as separator. */
853 blacklist_store_name (const char *name
, ent_t
*ent
)
855 int namelen
= strlen (name
);
858 /* first call, setup cache */
859 if (ent
->blacklist
.size
== 0)
861 ent
->blacklist
.size
= MAX (BLACKLIST_INITIAL_SIZE
, 2 * namelen
);
862 ent
->blacklist
.data
= malloc (ent
->blacklist
.size
);
863 if (ent
->blacklist
.data
== NULL
)
865 ent
->blacklist
.data
[0] = '|';
866 ent
->blacklist
.data
[1] = '\0';
867 ent
->blacklist
.current
= 1;
871 if (in_blacklist (name
, namelen
, ent
))
872 return; /* no duplicates */
874 if (ent
->blacklist
.current
+ namelen
+ 1 >= ent
->blacklist
.size
)
876 ent
->blacklist
.size
+= MAX (BLACKLIST_INCREMENT
, 2 * namelen
);
877 tmp
= realloc (ent
->blacklist
.data
, ent
->blacklist
.size
);
880 free (ent
->blacklist
.data
);
881 ent
->blacklist
.size
= 0;
884 ent
->blacklist
.data
= tmp
;
888 tmp
= stpcpy (ent
->blacklist
.data
+ ent
->blacklist
.current
, name
);
891 ent
->blacklist
.current
+= namelen
+ 1;
896 /* Returns TRUE if ent->blacklist contains name, else FALSE. */
898 in_blacklist (const char *name
, int namelen
, ent_t
*ent
)
900 char buf
[namelen
+ 3];
903 if (ent
->blacklist
.data
== NULL
)
907 cp
= stpcpy (&buf
[1], name
);
910 return strstr (ent
->blacklist
.data
, buf
) != NULL
;